//@Description Sketch to construct 32-bit Morton numbers from two 16-bit inputs. // jburnim_morton.sk // Author: jburnim@cs.berkeley.edu (Jacob Burnim) // // This file contains a sketch for constructing 32-bit Morton numbers from // two 16-bit inputs. "Interleaved bits (aka Morton numbers) are useful for // linearizing 2D integer coordinates, so x and y are combined into a single // number that can be compared easily and has the property that a number is // usually close to another if their x and y values are close." [1] // // Synthesized code is shown in comments at the end of the file. // // [1] Anderson, Bit Twiddling Hacks, http://www.cs.utk.edu/~vose/c-stuff/bithacks.html int W = 16; bit[2*W] interleave_bits(bit[W] x, bit[W] y) { bit[2*W] ret = 0; for (int i = 0; i < W; i++) { ret[i*2] = x[i]; ret[i*2+1] = y[i]; } return ret; } bit[2*W] sketch(bit[W] x, bit[W] y) implements interleave_bits { // Idea: It should be possible to shift all of the bits "in parallel" // using only log2(W) shifts. bit[2*W] x2 = x; int pt = 4*W; repeat(??) { int t = ??; // Shift some of the bits, and mask out their original positions. x2 = (x2 | (x2 << t)) & ??; assert t < pt; pt = t; } bit[2*W] y2 = y; repeat(??) { // Shift some of the bits, and mask out their original positions. y2 = (y2 | (y2 << ??)) & ??; } return x2 | (y2 << 1); } // In roughly 22 minutes, synthesizes: // // void sketch(bitvec<16> x_0, bitvec<16> y_1, bitvec<32> & s_2) { // bitvec<32> x2_3 = (x_0 | (x_0 << 8U)) & bitvec<32>("11111111000000001111111100000000"); // x2_3 = (x2_3 | (x2_3 << 4U)) & bitvec<32>("11110000111100001111000011110000"); // x2_3 = (x2_3 | (x2_3 << 2U)) & bitvec<32>("11001100110011001100110011001100"); // x2_3 = (x2_3 | (x2_3 << 1U)) & bitvec<32>("10101010101010101010101010101010"); // bitvec<32> y2_4 = (y_1 | (y_1 << 8U)) & bitvec<32>("11111111000000001111111100000000"); // y2_4 = (y2_4 | (y2_4 << 4U)) & bitvec<32>("11110000111100001111000011110000"); // y2_4 = (y2_4 | (y2_4 << 2U)) & bitvec<32>("11001100110011001100110011001100"); // y2_4 = (y2_4 | (y2_4 << 1U)) & bitvec<32>("10101010101010101010101010101010"); // s_2 = x2_3 | (y2_4 << 1U); // } // // NOTE: This sketch becomes much easier if we tell the synthesizer that the // constants for processing 'y' are the same as for 'x'. See file // jburnim_morton_easier.sk. // Sketches for W = 8 -- // // In roughly one minute, synthesizes: // // void sketch(bitvec<8> x_0, bitvec<8> y_1, bitvec<16> & s_2) { // bitvec<16> x2_3 = (x_0 | (x_0 << 4U)) & bitvec<16>("1111000011110000"); // x2_3 = (x2_3 | (x2_3 << 2U)) & bitvec<16>("1100110011001100"); // x2_3 = (x2_3 | (x2_3 << 1U)) & bitvec<16>("1010101010101010"); // bitvec<16> y2_4 = (y_1 | (y_1 << 4U)) & bitvec<16>("1111000011110000"); // y2_4 = (y2_4 | (y2_4 << 2U)) & bitvec<16>("1100110011001100"); // y2_4 = (y2_4 | (y2_4 << 1U)) & bitvec<16>("1010101010101010"); // s_2 = x2_3 | (y2_4 << 1U); // } // // Strangely, a later synthesis ran for 3.5 minutes and produced a longer, // less efficient solution: // // void sketch(bitvec<8> x_0, bitvec<8> y_1, bitvec<16> & s_2) { // bitvec<16> x2_3 = (x_0 | (x_0 << 4U)) & bitvec<16>("1111000011110000"); // x2_3 = (x2_3 | (x2_3 << 2U)) & bitvec<16>("1100110011001100"); // x2_3 = (x2_3 | (x2_3 << 16U)) & bitvec<16>("1100110011001100"); // x2_3 = (x2_3 | (x2_3 << 1U)) & bitvec<16>("1010101010101010"); // bitvec<16> y2_4 = (y_1 | (y_1 << 4U)) & bitvec<16>("1111000011110000"); // y2_4 = (y2_4 | (y2_4 << 2U)) & bitvec<16>("1100110011001100"); // y2_4 = (y2_4 | (y2_4 << 16U)) & bitvec<16>("1100110011001100"); // y2_4 = (y2_4 | (y2_4 << 1U)) & bitvec<16>("1010101010101010"); // s_2 = x2_3 | (y2_4 << 1U); // }