Skip to content

Instantly share code, notes, and snippets.

@tomprimozic
Created November 22, 2013 00:56
Show Gist options
  • Save tomprimozic/7592841 to your computer and use it in GitHub Desktop.
Save tomprimozic/7592841 to your computer and use it in GitHub Desktop.
Playing with Terra - a simple object system with unboxed floats
stdlib = terralib.includec("stdlib.h")
stdio = terralib.includec("stdio.h")
struct void {}
any = &&void
struct Type {name : rawstring}
StringType = global(&Type)
IntType = global(&Type)
FloatType = global(&Type)
local typeof = macro(function(obj)
return `[&&Type](obj)[-1]
end)
-- 64-bit floating point number representation:
--
-- | s | eeeeeeeeeee | fff ... fff |
--
-- where `s` is the sign bit, `e` are 11 exponent bits,
-- and `f` are 52 fraction bits. If all exponent bits are 1,
-- the number is a infinity (if fraction is 0) or NaN (otherwise).
--
-- On x64 platforms, only one kind of NaN is generated:
--
-- NaN = fff8000000000000
--
-- On the other hand, pointers allocated in the user-space memory
-- have the first 17 bits set to zero; the "highest" pointer value is
--
-- max_ptr = 00007fffffffffff
--
-- Therefore, we can overlap doubles, pointers and other data types,
-- as long as double's bits are inverted, and we know that
--
-- x >= 0007ffffffffffff => (not x) : double
-- x <= 00007fffffffffff => x : ptr
struct double_and_bits { union { value : double, bits : uint64 } }
local double_to_bits = macro(function(obj)
return `[double_and_bits]({value = obj}).bits
end)
local bits_to_double = macro(function(obj)
return `[double_and_bits]({bits = obj}).value
end)
local is_fastdouble = macro(function(obj)
return `[bool]([uint64](obj) >= 0x0007ffffffffffffLL)
end)
local to_fastdouble = macro(function(obj)
return `[any](not double_to_bits(obj))
end)
local from_fastdouble = macro(function(obj)
return `bits_to_double(not [uint64](obj))
end)
terra get_type(obj : any)
if is_fastdouble(obj) then
return FloatType
else
return typeof(obj)
end
end
terra init()
StringType = [&Type](stdlib.malloc(sizeof(Type)))
StringType.name = "string"
IntType = [&Type](stdlib.malloc(sizeof(Type)))
IntType.name = "int"
FloatType = [&Type](stdlib.malloc(sizeof(Type)))
FloatType.name = "float"
end
terra alloc_object(t : &Type, size : int64)
var memory = stdlib.malloc(size + sizeof([&Type]))
var obj = [any](memory) + 1
typeof(obj) = t
return obj
end
terra print_type(obj : any)
stdio.printf("type: %s\n", get_type(obj).name)
end
terra print_any(obj : any)
var t = get_type(obj)
if t == StringType then
stdio.printf("<string> %s\n", obj)
elseif t == IntType then
stdio.printf("<int> %lld\n", @obj)
elseif t == FloatType then
stdio.printf("<float> %f\n", from_fastdouble(obj))
else
stdio.printf("<unknown type>\n")
end
end
terra main()
stdio.printf(" init\n")
init()
stdio.printf(" alloc\n")
var obj_str = alloc_object(StringType, 4)
[&int8](obj_str)[0] = 97 -- "a"
[&int8](obj_str)[1] = 98 -- "b"
[&int8](obj_str)[2] = 99 -- "c"
[&int8](obj_str)[3] = 0 -- null terminator
var obj_int = alloc_object(IntType, sizeof(int64))
@[&int64](obj_int) = 112
var obj_float = to_fastdouble(1024.125)
stdio.printf(" print type\n")
print_type(obj_str)
print_type(obj_int)
print_type(obj_float)
stdio.printf(" print object\n")
print_any(obj_str)
print_any(obj_int)
print_any(obj_float)
stdio.printf(" end\n")
end
--main()
terralib.saveobj("obj", {main = main})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment