418fd0039e
Signed-off-by: Jakub Doka <jakub.doka2@gmail.com>
2039 lines
37 KiB
Markdown
2039 lines
37 KiB
Markdown
# HERE SHALL THE DOCUMENTATION RESIDE
|
|
|
|
## Enforced Political Views
|
|
|
|
- worse is better
|
|
- less is more
|
|
- embrace `unsafe {}`
|
|
- adhere `macro_rules!`
|
|
- pessimization == death (put in `std::pin::Pin` and left with hungry crabs)
|
|
- importing external dependencies == death (`fn(dependencies) -> ExecutionStrategy`)
|
|
- above sell not be disputed, discussed, or questioned
|
|
|
|
## What hblang is
|
|
|
|
Holey-Bytes-Language (hblang for short) (*.hb) is the only true language targeting hbvm byte code. hblang is low level, manually managed, and procedural. Its rumored to be better then writing hbasm and you should probably use it for complex applications.
|
|
|
|
## What hblang isnt't
|
|
|
|
hblang knows what it isn't, because it knows what it is, hblang computes this by sub...
|
|
|
|
## Examples
|
|
|
|
Examples are also used in tests. To add an example that runs during testing add:
|
|
<pre>
|
|
#### <name>
|
|
```hb
|
|
<example>
|
|
```
|
|
</pre>
|
|
and also:
|
|
```rs
|
|
<name>;
|
|
```
|
|
to the `run_tests` macro at the bottom of the `src/son.rs`.
|
|
|
|
### Tour Examples
|
|
|
|
Following examples incrementally introduce language features and syntax.
|
|
|
|
#### main_fn
|
|
```hb
|
|
main := fn(): uint {
|
|
return 1
|
|
}
|
|
```
|
|
|
|
#### arithmetic
|
|
```hb
|
|
main := fn(): uint {
|
|
return 10 - 20 / 2 + 4 * (2 + 2) - 4 * 4 + (true << 0) + -1
|
|
}
|
|
```
|
|
|
|
|
|
#### floating_point_arithmetic
|
|
```hb
|
|
main := fn(): f32 {
|
|
return 10. - 20. / 2. + 4. * (2. + 2.) - 4. * 4. + -1.
|
|
}
|
|
```
|
|
|
|
#### functions
|
|
```hb
|
|
main := fn(): uint {
|
|
return add_one(10) + add_two(20)
|
|
}
|
|
|
|
add_two := fn(x: uint): uint {
|
|
return x + 2
|
|
}
|
|
|
|
add_one := fn(x: uint): uint {
|
|
return x + 1
|
|
}
|
|
```
|
|
|
|
#### comments
|
|
```hb
|
|
// commant is an item
|
|
main := fn(): uint {
|
|
// comment is a statement
|
|
|
|
foo(/* comment is an exprression /* if you are crazy */ */)
|
|
return 0
|
|
}
|
|
|
|
foo := fn(comment: void): void return /* comment evaluates to void */
|
|
|
|
// comments might be formatted in the future
|
|
```
|
|
|
|
#### if_statements
|
|
```hb
|
|
main := fn(): uint {
|
|
return fib(10)
|
|
}
|
|
|
|
fib := fn(x: uint): uint {
|
|
if x <= 2 {
|
|
return 1
|
|
} else {
|
|
return fib(x - 1) + fib(x - 2)
|
|
}
|
|
}
|
|
```
|
|
|
|
#### variables
|
|
```hb
|
|
main := fn(): uint {
|
|
ඞ := 1
|
|
b := 2
|
|
ඞ += 1
|
|
return ඞ - b
|
|
}
|
|
```
|
|
|
|
#### hex_octal_binary_literals
|
|
```hb
|
|
main := fn(): uint {
|
|
hex := 0xFF
|
|
decimal := 255
|
|
octal := 0o377
|
|
binary := 0b11111111
|
|
ascii := '\n'
|
|
|
|
if hex == decimal & octal == decimal & binary == decimal {
|
|
return 0
|
|
}
|
|
|
|
return 1
|
|
}
|
|
```
|
|
|
|
#### loops
|
|
```hb
|
|
main := fn(): uint {
|
|
return fib(10)
|
|
}
|
|
|
|
fib := fn(n: uint): uint {
|
|
a := 0
|
|
b := 1
|
|
loop if n == 0 break else {
|
|
c := a + b
|
|
a = b
|
|
b = c
|
|
n -= 1
|
|
}
|
|
return a
|
|
}
|
|
```
|
|
|
|
#### pointers
|
|
```hb
|
|
main := fn(): uint {
|
|
a := 1
|
|
b := &a
|
|
|
|
boundary := 1000
|
|
|
|
b = b + boundary - 2
|
|
b = b - (boundary - 2)
|
|
|
|
modify(b)
|
|
drop(a)
|
|
return *b - 2
|
|
}
|
|
|
|
modify := fn(a: ^uint): void {
|
|
*a = 2
|
|
return
|
|
}
|
|
|
|
drop := fn(a: uint): void {
|
|
return
|
|
}
|
|
```
|
|
|
|
#### structs
|
|
```hb
|
|
Ty := struct {
|
|
// comment
|
|
|
|
a: uint,
|
|
}
|
|
|
|
Ty2 := struct {
|
|
ty: Ty,
|
|
c: uint = 3,
|
|
}
|
|
|
|
useless := struct {}
|
|
|
|
main := fn(): uint {
|
|
// `packed` structs have no padding (all fields are alighred to 1)
|
|
if @sizeof(packed struct {a: u8, b: u16}) != 3 {
|
|
return 9001
|
|
}
|
|
|
|
finst := Ty2.{ty: .{a: 4}}
|
|
inst := odher_pass(finst)
|
|
if inst.c == 3 {
|
|
return pass(&inst.ty)
|
|
}
|
|
return 0
|
|
}
|
|
|
|
pass := fn(t: ^Ty): uint {
|
|
return t.a
|
|
}
|
|
|
|
odher_pass := fn(t: Ty2): Ty2 {
|
|
return t
|
|
}
|
|
```
|
|
|
|
#### tuples
|
|
```hb
|
|
main := fn(): uint {
|
|
tupl := .(1, 1)
|
|
return tupl[0] - tupl[1]
|
|
}
|
|
```
|
|
|
|
#### struct_scopes
|
|
```hb
|
|
$zr := 0
|
|
|
|
Struct := struct {
|
|
a: uint,
|
|
b: uint,
|
|
|
|
$zero := Self.(zr, zr)
|
|
|
|
$new := fn(a: uint, b: uint): Self return .(a, b)
|
|
|
|
$swap := fn(s: ^Self): void {
|
|
_ = diff(*s)
|
|
t := s.a
|
|
s.a = s.b
|
|
s.b = t
|
|
}
|
|
|
|
$diff := fn(s: Self): uint return s.a - s.b
|
|
}
|
|
|
|
main := fn(): uint {
|
|
z := Struct.zero
|
|
z += Struct.new(1, 2)
|
|
z.swap()
|
|
return z.diff()
|
|
}
|
|
```
|
|
|
|
#### enums
|
|
```hb
|
|
Enum := enum {
|
|
A,
|
|
B,
|
|
C,
|
|
|
|
$default := Self.A
|
|
}
|
|
|
|
some_enum := fn(): Enum return .A
|
|
|
|
main := fn(): uint {
|
|
e := some_enum()
|
|
|
|
match e {
|
|
Enum.default => return 0,
|
|
_ => return 100,
|
|
}
|
|
}
|
|
```
|
|
|
|
#### unions
|
|
```hb
|
|
Union := union {
|
|
i: u32,
|
|
f: f32,
|
|
|
|
$sconst := 0
|
|
|
|
$new := fn(i: u32): Self {
|
|
return .{i}
|
|
}
|
|
}
|
|
|
|
main := fn(): uint {
|
|
v := Union.{f: 0}
|
|
u := Union.new(Union.sconst)
|
|
return v.i + u.i
|
|
}
|
|
```
|
|
|
|
#### nullable_types
|
|
```hb
|
|
main := fn(): uint {
|
|
a := &1
|
|
|
|
b: ?^uint = null
|
|
if decide() b = a
|
|
|
|
if b == null return 9001
|
|
|
|
c: ?uint = *b
|
|
if decide() c = null
|
|
|
|
if c != null return 42
|
|
|
|
d: ?u16 = null
|
|
if decide() d = 1
|
|
|
|
if d == null return 69
|
|
|
|
sf := new_foo()
|
|
if sf == null return 999
|
|
str := "foo\0"
|
|
use_foo(sf, str)
|
|
|
|
f := no_foo()
|
|
|
|
if decide() f = .(a, 1)
|
|
|
|
if f == null return 34
|
|
|
|
bar := new_bar(a)
|
|
|
|
if decide() bar = null
|
|
|
|
if bar != null return 420
|
|
|
|
g: ?^uint = null
|
|
g = a
|
|
|
|
_rd := *g
|
|
|
|
return d - *f.a
|
|
}
|
|
|
|
Foo := struct {a: ^uint, b: uint}
|
|
no_foo := fn(): ?Foo return null
|
|
new_foo := fn(): ?Foo return .(&0, 0)
|
|
use_foo := fn(foo: Foo, str: ^u8): void {
|
|
}
|
|
|
|
Bar := struct {a: ?^uint, b: uint}
|
|
new_bar := fn(a: ?^uint): ?Bar return .(a, 1)
|
|
|
|
decide := fn(): bool return !false
|
|
```
|
|
|
|
#### inline_return_stack
|
|
```hb
|
|
$fun := fn(): [3]uint {
|
|
res := .[0, 1, 2]
|
|
return res
|
|
}
|
|
|
|
main := fn(): uint {
|
|
vl := fun()
|
|
return vl[0]
|
|
}
|
|
```
|
|
|
|
#### struct_operators
|
|
```hb
|
|
Point := struct {
|
|
x: uint,
|
|
y: uint,
|
|
}
|
|
|
|
Rect := struct {
|
|
a: Point,
|
|
b: Point,
|
|
}
|
|
|
|
Color := packed struct {b: u8, g: u8, r: u8, a: u8}
|
|
|
|
main := fn(): uint {
|
|
i := Color.(0, 0, 0, 0)
|
|
i += .(1, 1, 1, 1)
|
|
if i.r + i.g + i.b + i.a != 4 return 1008
|
|
|
|
if Point.(1, 1) != Point.(1, 1) return 1009
|
|
if Point.(1, 2) == Point.(1, 1) return 1010
|
|
if Point.(1, 2) < Point.(1, 1) return 1011
|
|
if Point.(1, 1) > Point.(2, 1) return 1012
|
|
|
|
a := Point.(1, 2)
|
|
b := Point.(3, 4)
|
|
|
|
d := Rect.(a + b, b - a)
|
|
zp := Point.(0, 0)
|
|
d2 := Rect.(zp - b, a)
|
|
d2 += d
|
|
|
|
c := d2.a + d2.b
|
|
return c.x + c.y
|
|
}
|
|
```
|
|
|
|
#### global_variables
|
|
```hb
|
|
global_var := 10
|
|
|
|
complex_global_var := fib(global_var) - 5
|
|
|
|
fib := fn(n: uint): uint {
|
|
if 2 > n {
|
|
return n
|
|
}
|
|
return fib(n - 1) + fib(n - 2)
|
|
}
|
|
|
|
main := fn(): uint {
|
|
complex_global_var += 5
|
|
return complex_global_var
|
|
}
|
|
```
|
|
note: values of global variables are evaluated at compile time
|
|
|
|
#### constants
|
|
```hb
|
|
main := fn(): u32 {
|
|
return SOME_CONST + 35
|
|
}
|
|
|
|
$SOME_CONST := 34
|
|
```
|
|
|
|
#### directives
|
|
```hb
|
|
foo := @use("foo.hb")
|
|
|
|
main := fn(): uint {
|
|
if false {
|
|
@error("unexpected '\0", u8, "', wah now?\0")
|
|
}
|
|
byte := @as(@ChildOf(^u8), 10)
|
|
_ = sum(byte, byte)
|
|
same_type_as_byte := @as(@TypeOf(byte), 30)
|
|
wide_uint := @as(u32, 40)
|
|
_ = sum(wide_uint, wide_uint)
|
|
truncated_uint := @as(u8, @intcast(wide_uint))
|
|
widened_float := @as(f64, @floatcast(1.))
|
|
int_from_float := @as(int, @fti(1.))
|
|
float_from_int := @as(f64, @itf(1))
|
|
size_of_Type_in_bytes := @sizeof(foo.Type)
|
|
align_of_Type_in_bytes := @alignof(foo.Type)
|
|
hardcoded_pointer := @as(^u8, @bitcast(10))
|
|
ecall_that_returns_uint := @as(uint, @eca(1, foo.Type.(10, 20), 5, 6))
|
|
embedded_array := @as([15]u8, @embed("text.txt"))
|
|
two_fields := @lenof(foo.Type)
|
|
string_length := @lenof("foo\0")
|
|
the_struct_kind := @kindof(foo.Type)
|
|
return @inline(foo.foo)
|
|
}
|
|
|
|
$sum := fn(a: @Any(), b: @TypeOf(a)): @TypeOf(a) return a + b
|
|
|
|
// in module: foo.hb
|
|
|
|
Type := struct {
|
|
brah: uint,
|
|
blah: uint,
|
|
}
|
|
|
|
foo := fn(): uint return 0
|
|
|
|
// in module: text.txt
|
|
arbitrary text
|
|
```
|
|
|
|
- `@use(<string>)`: imports a module based on relative path, cycles are allowed when importing
|
|
- `@TypeOf(<expr>)`: results into literal type of whatever the type of `<expr>` is, `<expr>` is not included in final binary
|
|
- `@as(<ty>, <expr>)`: hint to the compiler that `@TypeOf(<expr>) == <ty>`
|
|
- `@intcast(<expr>)`: needs to be used when conversion of `@TypeOf(<expr>)` would loose precision (widening of integers is implicit)
|
|
- `@sizeof(<ty>), @alignof(<ty>)`: get size and align of a type in bytes
|
|
- `@bitcast(<expr>)`: tell compiler to assume `@TypeOf(<expr>)` is whatever is inferred, so long as size matches
|
|
- `@eca(...<expr>)`: invoke `eca` instruction, where return type is inferred and `<expr>...` are arguments passed to the call in the standard call convention
|
|
- `@embed(<string>)`: include relative file as an array of bytes
|
|
- `@inline(<func>, ...<args>)`: equivalent to `<func>(...<args>)` but function is guaranteed to inline, compiler will otherwise never inline
|
|
- `@lenof(<ty>)`: reports a length of the type of indexing purposes or length ot a string constant
|
|
- `@kindof(<ty>)`: gives an u8 integer describing the kind of type as an index to array `[Builtin, Struct, Tuple, Enum, Union, Ptr, Slice, Opt, Func, Template, Global, Const, Module]`
|
|
- `@Any()`: generic parameter based on inference, TBD: this will ake arguments in the future that restrict what is accepted
|
|
- `@error(...<expr>)`: emit compiler error, if reachable, and use arguments to construct a message, can display strings and types
|
|
- `@ChildOf(<ty>)`: returns the child type of the `<ty>`, works for pointers and optionals (`@ChildOf(?u8) == u8`)
|
|
|
|
#### c_strings
|
|
```hb
|
|
str_len := fn(str: ^u8): uint {
|
|
len := 0
|
|
loop if *str == 0 break else {
|
|
len += 1
|
|
str += 1
|
|
}
|
|
return len
|
|
}
|
|
|
|
main := fn(): uint {
|
|
// when string ends with '\0' its a C string and thus type is '^u8'
|
|
some_str := "abඞ\n\r\t\{35}\{36373839}\0"
|
|
len := str_len(some_str)
|
|
some_other_str := "fff\0"
|
|
lep := str_len(some_other_str)
|
|
return lep + len
|
|
}
|
|
```
|
|
|
|
#### struct_patterns
|
|
```hb
|
|
.{fib, fib_iter, Fiber} := @use("fibs.hb")
|
|
|
|
main := fn(): uint {
|
|
.{a, b} := Fiber.{a: 10, b: 10}
|
|
return fib(a) - fib_iter(b)
|
|
}
|
|
|
|
// in module: fibs.hb
|
|
|
|
Fiber := struct {a: u8, b: u8}
|
|
|
|
fib := fn(n: uint): uint if n < 2 {
|
|
return n
|
|
} else {
|
|
return fib(n - 1) + fib(n - 2)
|
|
}
|
|
|
|
fib_iter := fn(n: uint): uint {
|
|
a := 0
|
|
b := 1
|
|
loop if n == 0 break else {
|
|
c := a + b
|
|
a = b
|
|
b = c
|
|
n -= 1
|
|
}
|
|
return a
|
|
}
|
|
```
|
|
|
|
#### arrays
|
|
```hb
|
|
main := fn(): uint {
|
|
addr: u16 = 0x1FF
|
|
msg := u8.[0, 0, @intcast(addr), @intcast(addr >> 8)]
|
|
_force_stack := &msg
|
|
|
|
arr := .[1, 2, 4]
|
|
return pass(&arr) + msg[3]
|
|
}
|
|
|
|
pass := fn(arr: ^[3]uint): uint {
|
|
return arr[0] + arr[1] + arr[arr[1]]
|
|
}
|
|
```
|
|
|
|
#### inline
|
|
```hb
|
|
main := fn(): uint {
|
|
some_eca()
|
|
return @inline(foo, 1, 2, 3) - bar(3)
|
|
}
|
|
|
|
$some_eca := fn(): void return @eca(8)
|
|
|
|
// only for functions with no control flow (if, loop)
|
|
$bar := fn(a: uint): uint return a * 2
|
|
|
|
gb := 0
|
|
|
|
foo := fn(a: uint, b: uint, c: uint): uint {
|
|
if false | gb != 0 return 1
|
|
return a + b + c
|
|
}
|
|
```
|
|
|
|
#### idk
|
|
```hb
|
|
_edge_case: uint = idk
|
|
|
|
main := fn(): uint {
|
|
big_array: [128]u8 = idk
|
|
i := 0
|
|
loop if i >= 128 break else {
|
|
big_array[i] = 69
|
|
i += 1
|
|
}
|
|
return big_array[42]
|
|
}
|
|
```
|
|
|
|
#### generic_functions
|
|
```hb
|
|
add := fn($T: type, a: T, b: T): T {
|
|
if T != void {
|
|
return a + b
|
|
}
|
|
}
|
|
|
|
main := fn(): uint {
|
|
add(void, {
|
|
}, {
|
|
})
|
|
return add(u32, 2, 2) - add(uint, 1, 3)
|
|
}
|
|
```
|
|
|
|
#### generic_types
|
|
```hb
|
|
malloc_sys_call := 69
|
|
free_sys_call := 96
|
|
|
|
malloc := fn(size: uint, align: uint): ?^void return @eca(malloc_sys_call, size, align)
|
|
free := fn(ptr: ^void, size: uint, align: uint): void return @eca(free_sys_call, ptr, size, align)
|
|
|
|
Vec := fn($Elem: type): type return struct {
|
|
data: ^Elem,
|
|
len: uint,
|
|
cap: uint,
|
|
|
|
new := fn(): Self return .{data: @bitcast(@alignof(Elem)), len: 0, cap: 0}
|
|
|
|
deinit := fn(vec: ^Self): void {
|
|
free(@bitcast(vec.data), vec.cap * @sizeof(Elem), @alignof(Elem));
|
|
*vec = new()
|
|
return
|
|
}
|
|
|
|
push := fn(vec: ^Self, value: Elem): ?^Elem {
|
|
if vec.len == vec.cap {
|
|
if vec.cap == 0 {
|
|
vec.cap = 1
|
|
} else {
|
|
vec.cap *= 2
|
|
}
|
|
|
|
new_alloc: ?^Elem = @bitcast(malloc(vec.cap * @sizeof(Elem), @alignof(Elem)))
|
|
if new_alloc == null return null
|
|
|
|
src_cursor := vec.data
|
|
dst_cursor: ^Elem = new_alloc
|
|
end := vec.data + vec.len
|
|
|
|
loop if src_cursor == end break else {
|
|
*dst_cursor = *src_cursor
|
|
src_cursor += 1
|
|
dst_cursor += 1
|
|
}
|
|
|
|
if vec.len != 0 {
|
|
free(@bitcast(vec.data), vec.len * @sizeof(Elem), @alignof(Elem))
|
|
}
|
|
vec.data = new_alloc
|
|
}
|
|
|
|
slot := vec.data + vec.len;
|
|
*slot = value
|
|
vec.len += 1
|
|
return slot
|
|
}
|
|
}
|
|
|
|
main := fn(): uint {
|
|
vec := Vec(uint).new()
|
|
_ = vec.push(35)
|
|
defer vec.deinit()
|
|
vec2 := Vec(u8).new()
|
|
_ = vec2.push(34)
|
|
defer vec2.deinit()
|
|
return *vec.data + *vec2.data
|
|
}
|
|
```
|
|
|
|
#### die
|
|
```hb
|
|
|
|
fun := fn(): never die
|
|
|
|
main := fn(): never {
|
|
// simply emmits 'un' instruction that immediately terminates the execution
|
|
// the expresion has similar properties to 'return' but does not accept a value
|
|
fun()
|
|
}
|
|
```
|
|
|
|
#### defer
|
|
```hb
|
|
main := fn(): uint {
|
|
i := 0
|
|
loop {
|
|
defer i += 1
|
|
|
|
if i == 10 break
|
|
|
|
if i % 3 == 0 {
|
|
continue
|
|
}
|
|
}
|
|
return i - 11
|
|
}
|
|
```
|
|
|
|
#### unrolled_loops
|
|
```hb
|
|
Nums := struct {a: uint, b: u32, c: u16, d: u8}
|
|
|
|
main := fn(): uint {
|
|
nums := Nums.(1, 2, 3, 4)
|
|
i := 0
|
|
sum := 0
|
|
$loop if i == @lenof(Nums) break else {
|
|
sum += nums[i]
|
|
i += 1
|
|
}
|
|
return sum - 10
|
|
}
|
|
```
|
|
|
|
### Incomplete Examples
|
|
|
|
#### string_array
|
|
```hb
|
|
strings := (^u8).["abcdefshijklmnop\0", "abcdefghijklnnop\0", "abcdefshijklmnop\0", "abcdefghijklmnop\0", "abcdefghijflmnop\0", "dbcdefghijklmnop\0", "abcdefghijklmnop\0"]
|
|
|
|
main := fn(): uint {
|
|
return @bitcast(strings[0])
|
|
}
|
|
```
|
|
|
|
#### comptime_pointers
|
|
```hb
|
|
main := fn(): uint {
|
|
$integer := 7
|
|
modify(&integer)
|
|
return integer
|
|
}
|
|
|
|
modify := fn($num: ^uint): void {
|
|
$: *num = 0
|
|
}
|
|
```
|
|
|
|
#### fb_driver
|
|
```hb
|
|
arm_fb_ptr := fn(): uint return 100
|
|
x86_fb_ptr := fn(): uint return 100
|
|
|
|
check_platform := fn(): uint {
|
|
return x86_fb_ptr()
|
|
}
|
|
|
|
set_pixel := fn(x: uint, y: uint, width: uint): uint {
|
|
return y * width + x
|
|
}
|
|
|
|
main := fn(): uint {
|
|
fb_ptr := check_platform()
|
|
width := 100
|
|
height := 30
|
|
x := 0
|
|
y := 0
|
|
//t := 0
|
|
i := 0
|
|
|
|
loop {
|
|
if x < height {
|
|
//t += set_pixel(x, y, height)
|
|
x += 1
|
|
i += 1
|
|
} else {
|
|
x = 0
|
|
y += 1
|
|
if set_pixel(x, y, height) != i return 0
|
|
if y == width break
|
|
}
|
|
}
|
|
return i
|
|
}
|
|
```
|
|
|
|
### Purely Testing Examples
|
|
|
|
#### proper_ident_propagation
|
|
```hb
|
|
A := fn($T: type): type return struct {a: T}
|
|
|
|
$make_a := fn(a: @Any()): A(@TypeOf(a)) {
|
|
return .(a)
|
|
}
|
|
|
|
$make_b := fn(a: @Any()): struct {b: @TypeOf(a)} {
|
|
return .(a)
|
|
}
|
|
|
|
main := fn(): uint {
|
|
a := make_a(100)
|
|
b := make_b(100)
|
|
return a.a - b.b
|
|
}
|
|
```
|
|
|
|
#### method_receiver_by_value
|
|
```hb
|
|
$log := fn(ptr: ^u8): void return @eca(37, ptr)
|
|
Struct := struct {
|
|
ptr: ^u8,
|
|
print := fn(self: Self, rhs: Self): void {
|
|
log(self.ptr)
|
|
log(rhs.ptr)
|
|
}
|
|
}
|
|
|
|
Struct2 := struct {
|
|
ptr: ^u8,
|
|
print2 := fn(self: ^Self, rhs: ^Self): void {
|
|
log(self.ptr)
|
|
log(rhs.ptr)
|
|
}
|
|
}
|
|
|
|
main := fn(): void {
|
|
lhs := Struct.("Hello, World!\0")
|
|
rhs := Struct.("Goodbye, World!\0")
|
|
lhs.print(rhs)
|
|
|
|
lhs2 := Struct2.("Hello, World!\0")
|
|
rhs2 := Struct2.("Goodbye, World!\0")
|
|
lhs2.print2(&rhs2)
|
|
}
|
|
```
|
|
|
|
#### comparing_floating_points
|
|
```hb
|
|
main := fn(): uint {
|
|
if box(1) < box(0) return 1
|
|
if box(1) <= box(0) return 2
|
|
if box(0) > box(1) return 3
|
|
if box(0) >= box(1) return 4
|
|
return 0
|
|
}
|
|
|
|
box := fn(v: f32): f32 return v
|
|
box2 := fn(v: f64): f64 return v
|
|
```
|
|
|
|
#### pointer_comparison
|
|
```hb
|
|
main := fn(): int {
|
|
a := 10
|
|
b := 20
|
|
|
|
if &a == &a {
|
|
return 10
|
|
} else {
|
|
return 20
|
|
}
|
|
}
|
|
```
|
|
|
|
#### wrong_dead_code_elimination
|
|
```hb
|
|
Color := struct {b: u8}
|
|
main := fn(): void {
|
|
color := Color.(0)
|
|
n: u8 = 1
|
|
loop {
|
|
if color.b == 255 | color.b == 0 {
|
|
n = -n
|
|
}
|
|
color.b += n
|
|
}
|
|
}
|
|
```
|
|
|
|
#### different_function_destinations
|
|
```hb
|
|
Stru := struct {a: uint, b: uint}
|
|
new_stru := fn(): Stru return .(0, 0)
|
|
|
|
glob_stru := Stru.(1, 1)
|
|
|
|
main := fn(): uint {
|
|
glob_stru = new_stru()
|
|
if glob_stru.a != 0 return 300
|
|
glob_stru = .(1, 1)
|
|
glob_stru = @inline(new_stru)
|
|
if glob_stru.a != 0 return 200
|
|
|
|
glob_stru = .(1, 1)
|
|
strus := Stru.[glob_stru, glob_stru, glob_stru]
|
|
i := 0
|
|
loop if i == 3 break else {
|
|
strus[i] = new_stru()
|
|
i += 1
|
|
}
|
|
if strus[2].a != 0 return 100
|
|
|
|
strus = Stru.[glob_stru, glob_stru, glob_stru]
|
|
i = 0
|
|
loop if i == 3 break else {
|
|
strus[i] = @inline(new_stru)
|
|
i += 1
|
|
}
|
|
if strus[2].a != 0 return 10
|
|
|
|
return 0
|
|
}
|
|
```
|
|
|
|
#### triggering_store_in_divergent_branch
|
|
```hb
|
|
opaque := fn(): uint {
|
|
return 1 << 31
|
|
}
|
|
|
|
main := fn(): void {
|
|
a := 0
|
|
loop if a >= opaque() break else {
|
|
valid := true
|
|
b := 0
|
|
loop if b >= opaque() break else {
|
|
if b == 1 << 16 {
|
|
valid = false
|
|
break
|
|
}
|
|
b += 1
|
|
}
|
|
if valid == false continue
|
|
a += 1
|
|
}
|
|
}
|
|
```
|
|
|
|
#### very_nested_loops
|
|
```hb
|
|
|
|
$H := 3
|
|
$W := 3
|
|
|
|
main := fn(): int {
|
|
y: int = 0
|
|
loop if y == H break else {
|
|
x: int = 0
|
|
loop if x == W break else {
|
|
c_r: f64 = @itf(x)
|
|
c_i: f64 = @itf(y)
|
|
if c_i * c_r >= 10.0 return 0
|
|
x += 1
|
|
}
|
|
y += 1
|
|
}
|
|
|
|
return 1
|
|
}
|
|
|
|
Color := struct {r: u8, g: u8, b: u8, a: u8}
|
|
Vec := struct {x: uint, y: uint}
|
|
|
|
put_pixel := fn(pos: Vec, color: Color): void {
|
|
}
|
|
```
|
|
|
|
#### generic_type_mishap
|
|
```hb
|
|
opaque := fn($Expr: type, ptr: ^?Expr): void {
|
|
}
|
|
|
|
process := fn($Expr: type): void {
|
|
optional := @as(?Expr, null)
|
|
timer := 1000
|
|
loop if timer > 0 {
|
|
opaque(Expr, &optional)
|
|
if optional != null return {
|
|
}
|
|
timer -= 1
|
|
}
|
|
return
|
|
}
|
|
|
|
main := fn(): void process(uint)
|
|
```
|
|
|
|
#### storing_into_nullable_struct
|
|
```hb
|
|
StructA := struct {b: StructB, c: ^uint, d: uint}
|
|
|
|
StructB := struct {g: ^uint, c: StructC}
|
|
|
|
StructC := struct {c: uint}
|
|
|
|
some_index := fn(): ?uint return 0
|
|
|
|
heap := u8.[0, 1, 2]
|
|
|
|
optionala := fn(): ?StructA {
|
|
i := some_index()
|
|
if i == null die
|
|
return .(.(&0, .(1)), &0, heap[i])
|
|
}
|
|
|
|
Struct := struct {inner: uint}
|
|
|
|
optional := fn(): ?Struct {
|
|
return .(10)
|
|
}
|
|
|
|
do_stuff := fn(arg: uint): uint {
|
|
return arg
|
|
}
|
|
|
|
just_read := fn(s: StructA): void {
|
|
}
|
|
|
|
main := fn(): uint {
|
|
a := optionala()
|
|
if a == null {
|
|
return 10
|
|
}
|
|
a.b.c = .(0)
|
|
just_read(a)
|
|
innera := do_stuff(a.b.c.c)
|
|
|
|
val := optional()
|
|
if val == null {
|
|
return 20
|
|
}
|
|
val.inner = 100
|
|
inner := do_stuff(val.inner)
|
|
return innera + inner
|
|
}
|
|
```
|
|
|
|
#### scheduling_block_did_dirty
|
|
```hb
|
|
Struct := struct {
|
|
pad: uint,
|
|
pad2: uint,
|
|
}
|
|
|
|
file := u8.[255]
|
|
|
|
opaque := fn(x: uint): uint {
|
|
return file[x]
|
|
}
|
|
|
|
constructor := fn(x: uint): Struct {
|
|
a := opaque(x)
|
|
return .(a, a)
|
|
}
|
|
|
|
main := fn(): void {
|
|
something := constructor(0)
|
|
return
|
|
}
|
|
```
|
|
|
|
#### null_check_returning_small_global
|
|
```hb
|
|
magic := 127
|
|
get := fn(file: ^u8): ?uint {
|
|
if *file == magic {
|
|
return magic
|
|
} else {
|
|
return null
|
|
}
|
|
}
|
|
|
|
some_file := u8.[127, 255, 255, 255, 255, 255]
|
|
|
|
foo := fn(): ?uint {
|
|
gotten := get(&some_file[0])
|
|
if gotten == null {
|
|
return null
|
|
} else if gotten == 4 {
|
|
return 2
|
|
} else if gotten == magic {
|
|
return 0
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
main := fn(): uint {
|
|
f := foo()
|
|
if f == null return 100
|
|
return f
|
|
}
|
|
```
|
|
|
|
#### null_check_in_the_loop
|
|
```hb
|
|
A := struct {
|
|
x_change: u8,
|
|
y_change: u8,
|
|
left: u8,
|
|
middle: u8,
|
|
right: u8,
|
|
}
|
|
|
|
return_fn := fn(): ?A {
|
|
return A.(0, 0, 0, 0, 0)
|
|
}
|
|
|
|
main := fn(): int {
|
|
loop {
|
|
ret := return_fn()
|
|
if ret != null {
|
|
return 1
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
#### stack_provenance
|
|
```hb
|
|
main := fn(): uint {
|
|
return *dangle()
|
|
}
|
|
dangle := fn(): ^uint return &0
|
|
```
|
|
|
|
#### advanced_floating_point_arithmetic
|
|
```hb
|
|
sin_table := f32.[0.0, 0.02454122852291229, 0.04906767432741801, 0.07356456359966743, 0.0980171403295606, 0.1224106751992162, 0.1467304744553617, 0.1709618887603012, 0.1950903220161282, 0.2191012401568698, 0.2429801799032639, 0.2667127574748984, 0.2902846772544623, 0.3136817403988915, 0.3368898533922201, 0.3598950365349881, 0.3826834323650898, 0.4052413140049899, 0.4275550934302821, 0.4496113296546065, 0.4713967368259976, 0.492898192229784, 0.5141027441932217, 0.5349976198870972, 0.5555702330196022, 0.5758081914178453, 0.5956993044924334, 0.6152315905806268, 0.6343932841636455, 0.6531728429537768, 0.6715589548470183, 0.6895405447370668, 0.7071067811865475, 0.7242470829514669, 0.7409511253549591, 0.7572088465064845, 0.773010453362737, 0.7883464276266062, 0.8032075314806448, 0.8175848131515837, 0.8314696123025452, 0.844853565249707, 0.8577286100002721, 0.8700869911087113, 0.8819212643483549, 0.8932243011955153, 0.9039892931234433, 0.9142097557035307, 0.9238795325112867, 0.9329927988347388, 0.9415440651830208, 0.9495281805930367, 0.9569403357322089, 0.9637760657954398, 0.970031253194544, 0.9757021300385286, 0.9807852804032304, 0.9852776423889412, 0.989176509964781, 0.99247953459871, 0.9951847266721968, 0.9972904566786902, 0.9987954562051724, 0.9996988186962042, 1.0, 0.9996988186962042, 0.9987954562051724, 0.9972904566786902, 0.9951847266721969, 0.99247953459871, 0.989176509964781, 0.9852776423889412, 0.9807852804032304, 0.9757021300385286, 0.970031253194544, 0.9637760657954398, 0.9569403357322089, 0.9495281805930367, 0.9415440651830208, 0.9329927988347388, 0.9238795325112867, 0.9142097557035307, 0.9039892931234434, 0.8932243011955152, 0.881921264348355, 0.8700869911087115, 0.8577286100002721, 0.8448535652497072, 0.8314696123025455, 0.8175848131515837, 0.8032075314806449, 0.7883464276266063, 0.7730104533627371, 0.7572088465064847, 0.740951125354959, 0.7242470829514669, 0.7071067811865476, 0.6895405447370671, 0.6715589548470186, 0.6531728429537766, 0.6343932841636455, 0.6152315905806269, 0.5956993044924335, 0.5758081914178454, 0.5555702330196022, 0.5349976198870972, 0.5141027441932218, 0.4928981922297841, 0.4713967368259979, 0.4496113296546069, 0.427555093430282, 0.4052413140049899, 0.3826834323650899, 0.3598950365349883, 0.3368898533922203, 0.3136817403988914, 0.2902846772544624, 0.2667127574748985, 0.2429801799032641, 0.21910124015687, 0.1950903220161286, 0.1709618887603012, 0.1467304744553618, 0.1224106751992163, 0.09801714032956083, 0.07356456359966773, 0.04906767432741797, 0.02454122852291233, 0.0, -0.02454122852291208, -0.04906767432741772, -0.0735645635996675, -0.09801714032956059, -0.1224106751992161, -0.1467304744553616, -0.170961888760301, -0.1950903220161284, -0.2191012401568698, -0.2429801799032638, -0.2667127574748983, -0.2902846772544621, -0.3136817403988912, -0.3368898533922201, -0.3598950365349881, -0.3826834323650897, -0.4052413140049897, -0.4275550934302818, -0.4496113296546067, -0.4713967368259976, -0.4928981922297839, -0.5141027441932216, -0.5349976198870969, -0.555570233019602, -0.5758081914178453, -0.5956993044924332, -0.6152315905806267, -0.6343932841636453, -0.6531728429537765, -0.6715589548470184, -0.6895405447370668, -0.7071067811865475, -0.7242470829514668, -0.7409511253549589, -0.7572088465064842, -0.7730104533627367, -0.7883464276266059, -0.8032075314806451, -0.8175848131515838, -0.8314696123025452, -0.844853565249707, -0.857728610000272, -0.8700869911087113, -0.8819212643483549, -0.8932243011955152, -0.9039892931234431, -0.9142097557035305, -0.9238795325112865, -0.932992798834739, -0.9415440651830208, -0.9495281805930367, -0.9569403357322088, -0.9637760657954398, -0.970031253194544, -0.9757021300385285, -0.9807852804032303, -0.9852776423889411, -0.9891765099647809, -0.9924795345987101, -0.9951847266721969, -0.9972904566786902, -0.9987954562051724, -0.9996988186962042, -1.0, -0.9996988186962042, -0.9987954562051724, -0.9972904566786902, -0.9951847266721969, -0.9924795345987101, -0.9891765099647809, -0.9852776423889412, -0.9807852804032304, -0.9757021300385286, -0.970031253194544, -0.96377606579544, -0.9569403357322089, -0.9495281805930368, -0.9415440651830209, -0.9329927988347391, -0.9238795325112866, -0.9142097557035306, -0.9039892931234433, -0.8932243011955153, -0.881921264348355, -0.8700869911087115, -0.8577286100002722, -0.8448535652497072, -0.8314696123025455, -0.817584813151584, -0.8032075314806453, -0.7883464276266061, -0.7730104533627369, -0.7572088465064846, -0.7409511253549591, -0.724247082951467, -0.7071067811865477, -0.6895405447370672, -0.6715589548470187, -0.6531728429537771, -0.6343932841636459, -0.6152315905806274, -0.5956993044924332, -0.5758081914178452, -0.5555702330196022, -0.5349976198870973, -0.5141027441932219, -0.4928981922297843, -0.4713967368259979, -0.449611329654607, -0.4275550934302825, -0.4052413140049904, -0.3826834323650904, -0.359895036534988, -0.33688985339222, -0.3136817403988915, -0.2902846772544625, -0.2667127574748986, -0.2429801799032642, -0.2191012401568702, -0.1950903220161287, -0.1709618887603018, -0.1467304744553624, -0.122410675199216, -0.09801714032956051, -0.07356456359966741, -0.04906767432741809, -0.02454122852291245]
|
|
|
|
sin := fn(theta: f32): f32 {
|
|
PI := 3.14159265358979323846
|
|
TABLE_SIZE := @as(i32, 256)
|
|
si := @fti(theta * 0.5 * @itf(TABLE_SIZE) / PI)
|
|
d := theta - @itf(si) * 2.0 * PI / @itf(TABLE_SIZE)
|
|
ci := si + TABLE_SIZE / 4 & TABLE_SIZE - 1
|
|
si &= TABLE_SIZE - 1
|
|
return sin_table[@bitcast(si)] + (sin_table[@bitcast(ci)] - 0.5 * sin_table[@bitcast(si)] * d) * d
|
|
}
|
|
|
|
main := fn(): int {
|
|
// expected result: 826
|
|
return @fti(sin(1000.0) * 1000.0)
|
|
}
|
|
```
|
|
|
|
#### nullable_structure
|
|
```hb
|
|
Structure := struct {}
|
|
|
|
BigStructure := struct {a: uint, b: uint}
|
|
|
|
MidStructure := struct {a: u8}
|
|
|
|
returner_fn := fn(): ?Structure {
|
|
return .()
|
|
}
|
|
returner_bn := fn(): ?BigStructure {
|
|
return .(0, 0)
|
|
}
|
|
returner_cn := fn(): ?MidStructure {
|
|
return .(0)
|
|
}
|
|
|
|
main := fn(): int {
|
|
ret := returner_fn()
|
|
bet := returner_bn()
|
|
cet := returner_cn()
|
|
if ret != null & bet != null & cet != null {
|
|
return 1
|
|
}
|
|
|
|
return 0
|
|
}
|
|
```
|
|
|
|
#### needless_unwrap
|
|
```hb
|
|
main := fn(): uint {
|
|
always_nn := @as(?^uint, &0)
|
|
ptr1 := @unwrap(always_nn)
|
|
always_n := @as(?^uint, null)
|
|
ptr2 := @unwrap(always_n)
|
|
return *ptr1 + *ptr2
|
|
}
|
|
```
|
|
|
|
#### optional_from_eca
|
|
```hb
|
|
main := fn(): uint {
|
|
a := @as(?uint, @eca(0, 0, 0, 0))
|
|
|
|
if a == null {
|
|
die
|
|
}
|
|
return a
|
|
}
|
|
```
|
|
|
|
#### returning_optional_issues
|
|
```hb
|
|
bmp := 0
|
|
|
|
get_format := fn(): ?uint {
|
|
return bmp
|
|
}
|
|
|
|
main := fn(): uint {
|
|
fmt := get_format()
|
|
if fmt == null {
|
|
return 1
|
|
} else {
|
|
return fmt
|
|
}
|
|
}
|
|
```
|
|
|
|
#### inlining_issues
|
|
```hb
|
|
main := fn(): void {
|
|
@use("main.hb").put_filled_rect(.(&.(0), 100, 100), .(0, 0), .(0, 0), .(1))
|
|
}
|
|
|
|
// in module: memory.hb
|
|
|
|
SetMsg := packed struct {a: u8, count: u32, size: u32, src: ^u8, dest: ^u8}
|
|
set := fn($Expr: type, src: ^Expr, dest: ^Expr, count: uint): void {
|
|
return @eca(8, 2, &SetMsg.(5, @intcast(count), @intcast(@sizeof(Expr)), @bitcast(src), @bitcast(dest)), @sizeof(SetMsg))
|
|
}
|
|
|
|
// in module: main.hb
|
|
|
|
Color := struct {r: u8}
|
|
|
|
Vec2 := fn($Ty: type): type return struct {x: Ty, y: Ty}
|
|
|
|
memory := @use("memory.hb")
|
|
|
|
Surface := struct {
|
|
buf: ^Color,
|
|
width: uint,
|
|
height: uint,
|
|
}
|
|
|
|
indexptr := fn(surface: Surface, x: uint, y: uint): ^Color {
|
|
return surface.buf + y * surface.width + x
|
|
}
|
|
|
|
put_filled_rect := fn(surface: Surface, pos: Vec2(uint), tr: Vec2(uint), color: Color): void {
|
|
top_start_idx := @inline(indexptr, surface, pos.x, pos.y)
|
|
bottom_start_idx := @inline(indexptr, surface, pos.x, pos.y + tr.y - 1)
|
|
rows_to_fill := tr.y
|
|
|
|
loop if rows_to_fill <= 1 break else {
|
|
@inline(memory.set, Color, &color, top_start_idx, @bitcast(tr.x))
|
|
@inline(memory.set, Color, &color, bottom_start_idx, @bitcast(tr.x))
|
|
|
|
top_start_idx += surface.width
|
|
bottom_start_idx -= surface.width
|
|
rows_to_fill -= 2
|
|
}
|
|
|
|
if rows_to_fill == 1 {
|
|
@inline(memory.set, Color, &color, top_start_idx, @bitcast(tr.x))
|
|
}
|
|
|
|
return
|
|
}
|
|
```
|
|
|
|
#### only_break_loop
|
|
```hb
|
|
memory := @use("memory.hb")
|
|
|
|
bar := fn(): int {
|
|
loop if memory.inb(0x64) != 0 return 1
|
|
}
|
|
|
|
foo := fn(): void {
|
|
loop if (memory.inb(0x64) & 2) == 0 break
|
|
memory.outb(0x60, 0x0)
|
|
}
|
|
|
|
main := fn(): int {
|
|
@inline(foo)
|
|
return @inline(bar)
|
|
}
|
|
|
|
// in module: memory.hb
|
|
inb := fn(f: int): int return f
|
|
outb := fn(f: int, g: int): void {
|
|
}
|
|
```
|
|
|
|
#### reading_idk
|
|
```hb
|
|
main := fn(): int {
|
|
a := @as(int, idk)
|
|
return a
|
|
}
|
|
```
|
|
|
|
#### nonexistent_ident_import
|
|
```hb
|
|
main := @use("foo.hb").main
|
|
// in module: foo.hb
|
|
foo := fn(): void {
|
|
return
|
|
}
|
|
foo := fn(): void {
|
|
return
|
|
}
|
|
main := @use("bar.hb").mian
|
|
// in module: bar.hb
|
|
main := fn(): void {
|
|
return
|
|
}
|
|
```
|
|
|
|
#### big_array_crash
|
|
```hb
|
|
sin_table := .[0, 174, 348, 523, 697, 871, 1045, 1218, 1391, 1564, 1736, 1908, 2079, 2249, 2419, 2588, 2756, 2923, 3090, 3255, 3420, 3583, 3746, 3907, 4067, 4226, 4384, 4540, 4695, 4848, 5000, 5150, 5299, 5446, 5591, 5735, 5877, 6018, 6156, 6293, 6427, 6560, 6691, 6819, 6946, 7071, 7193, 7313, 7431, 7547, 7660, 7771, 7880, 7986, 8090, 8191, 8290, 8386, 8480, 8571, 8660, 8746, 8829, 8910, 8987, 9063, 9135, 9205, 9271, 9335, 9396, 9455, 9510, 9563, 9612, 9659, 9702, 9743, 9781, 9816, 9848, 9877, 9902, 9925, 9945, 9961, 9975, 9986, 9993, 9998, 10000]
|
|
|
|
main := fn(): uint return sin_table[10]
|
|
```
|
|
|
|
#### returning_global_struct
|
|
```hb
|
|
Color := struct {r: u8, g: u8, b: u8, a: u8}
|
|
white := Color.(255, 255, 255, 255)
|
|
random_color := fn(): Color {
|
|
return white
|
|
}
|
|
main := fn(): uint {
|
|
val := random_color()
|
|
return @as(uint, val.r) + val.g + val.b + val.a
|
|
}
|
|
```
|
|
|
|
#### small_struct_bitcast
|
|
```hb
|
|
Color := struct {r: u8, g: u8, b: u8, a: u8}
|
|
white := Color.(255, 255, 255, 255)
|
|
u32_to_color := fn(v: u32): Color return @bitcast(u32_to_u32(@bitcast(v)))
|
|
u32_to_u32 := fn(v: u32): u32 return v
|
|
main := fn(): uint {
|
|
return u32_to_color(@bitcast(white)).r
|
|
}
|
|
```
|
|
|
|
#### small_struct_assignment
|
|
```hb
|
|
Color := struct {r: u8, g: u8, b: u8, a: u8}
|
|
white := Color.(255, 255, 255, 255)
|
|
black := Color.(0, 0, 0, 0)
|
|
main := fn(): uint {
|
|
f := black
|
|
f = white
|
|
return f.a
|
|
}
|
|
```
|
|
|
|
#### intcast_store
|
|
```hb
|
|
SetMsg := packed struct {a: u8, count: u32, size: u32, src: ^u8, dest: ^u8}
|
|
set := fn($Expr: type, src: ^Expr, dest: ^Expr, count: uint): u32 {
|
|
l := SetMsg.(5, @intcast(count), @intcast(@sizeof(Expr)), @bitcast(src), @bitcast(dest))
|
|
return l.count
|
|
}
|
|
|
|
main := fn(): uint {
|
|
return set(uint, &0, &0, 1024)
|
|
}
|
|
```
|
|
|
|
#### string_flip
|
|
```hb
|
|
U := struct {u: uint}
|
|
main := fn(): uint {
|
|
arr := @as([2 * 2]U, idk)
|
|
|
|
i := 0
|
|
loop if i == 2 * 2 break else {
|
|
arr[i] = .(i)
|
|
i += 1
|
|
}
|
|
|
|
i = 0
|
|
loop if i == 2 / 2 break else {
|
|
j := 0
|
|
loop if j == 2 break else {
|
|
a := i * 2 + j
|
|
b := (2 - i - 1) * 2 + j
|
|
tmp := arr[a]
|
|
arr[a] = arr[b]
|
|
arr[b] = tmp
|
|
j += 1
|
|
}
|
|
i += 1
|
|
}
|
|
|
|
return arr[2].u
|
|
}
|
|
```
|
|
|
|
#### memory_swap
|
|
```hb
|
|
U := struct {u: uint, v: uint, g: uint}
|
|
main := fn(): uint {
|
|
a := decide(0)
|
|
b := decide(1)
|
|
|
|
c := a
|
|
a = b
|
|
b = c
|
|
|
|
return b.u + a.u
|
|
}
|
|
|
|
decide := fn(u: uint): U return .(u, 0, 0)
|
|
```
|
|
|
|
#### wide_ret
|
|
```hb
|
|
OemIdent := struct {
|
|
dos_version: [8]u8,
|
|
dos_version_name: [8]u8,
|
|
}
|
|
|
|
Stru := struct {
|
|
a: u16,
|
|
b: u16,
|
|
}
|
|
|
|
small_struct := fn(): Stru {
|
|
return .{a: 0, b: 0}
|
|
}
|
|
|
|
maina := fn(major: uint, minor: uint): OemIdent {
|
|
_f := small_struct()
|
|
ver := u8.[0, 0, 0, 3, 1, 0, 0, 0]
|
|
return OemIdent.(ver, ver)
|
|
}
|
|
|
|
main := fn(): uint {
|
|
m := maina(0, 0)
|
|
return m.dos_version[3] - m.dos_version_name[4]
|
|
}
|
|
```
|
|
|
|
#### comptime_min_reg_leak
|
|
```hb
|
|
a := @use("math.hb").min(100, 50)
|
|
|
|
main := fn(): uint {
|
|
return a
|
|
}
|
|
|
|
// in module: math.hb
|
|
|
|
sizeof_uint := 32
|
|
shift := sizeof_uint - 1
|
|
min := fn(a: uint, b: uint): uint {
|
|
c := a - b
|
|
return b + (c & c >> shift)
|
|
}
|
|
```
|
|
|
|
#### different_types
|
|
```hb
|
|
Color := struct {
|
|
r: u8,
|
|
g: u8,
|
|
b: u8,
|
|
a: u8,
|
|
}
|
|
|
|
Point := struct {
|
|
x: u32,
|
|
y: u32,
|
|
}
|
|
|
|
Pixel := struct {
|
|
color: Color,
|
|
pouint: Point,
|
|
}
|
|
|
|
main := fn(): uint {
|
|
pixel := Pixel.{
|
|
color: Color.{
|
|
r: 255,
|
|
g: 0,
|
|
b: 0,
|
|
a: 255,
|
|
},
|
|
pouint: Point.{
|
|
x: 0,
|
|
y: 2,
|
|
},
|
|
}
|
|
|
|
soupan := 1
|
|
if *(&pixel.pouint.x + soupan) != 2 {
|
|
return 0
|
|
}
|
|
|
|
if *(&pixel.pouint.y - 1) != 0 {
|
|
return 64
|
|
}
|
|
|
|
return pixel.pouint.x + pixel.pouint.y + pixel.color.r
|
|
+ pixel.color.g + pixel.color.b + pixel.color.a
|
|
}
|
|
```
|
|
|
|
#### struct_return_from_module_function
|
|
```hb
|
|
bar := @use("bar.hb")
|
|
|
|
main := fn(): uint {
|
|
return 7 - bar.foo().x - bar.foo().y - bar.foo().z
|
|
}
|
|
|
|
// in module: bar.hb
|
|
|
|
|
|
foo := fn(): Foo {
|
|
return .{x: 3, y: 2, z: 2}
|
|
}
|
|
|
|
Foo := struct {x: uint, y: u32, z: u32}
|
|
```
|
|
|
|
#### sort_something_viredly
|
|
```hb
|
|
main := fn(): uint {
|
|
return sqrt(100)
|
|
}
|
|
|
|
sqrt := fn(x: uint): uint {
|
|
temp := 0
|
|
g := 0
|
|
b := 32768
|
|
bshift := 15
|
|
loop if b == 0 {
|
|
break
|
|
} else {
|
|
bshift -= 1
|
|
temp = b + (g << 1)
|
|
temp <<= bshift
|
|
if x >= temp {
|
|
g += b
|
|
x -= temp
|
|
}
|
|
b >>= 1
|
|
}
|
|
return g
|
|
}
|
|
```
|
|
|
|
#### struct_in_register
|
|
```hb
|
|
ColorBGRA := struct {b: u8, g: u8, r: u8, a: u8}
|
|
magenta := ColorBGRA.{b: 205, g: 0, r: 205, a: 255}
|
|
|
|
main := fn(): uint {
|
|
color := magenta
|
|
return color.r
|
|
}
|
|
```
|
|
|
|
#### comptime_function_from_another_file
|
|
```hb
|
|
stn := @use("stn.hb")
|
|
|
|
const_a := 100
|
|
const_b := 50
|
|
a := stn.math.min(const_a, const_b)
|
|
|
|
main := fn(): uint {
|
|
return a
|
|
}
|
|
|
|
// in module: stn.hb
|
|
math := @use("math.hb")
|
|
|
|
// in module: math.hb
|
|
sizeof_uint := 32
|
|
shift := sizeof_uint - 1
|
|
min := fn(a: uint, b: uint): uint {
|
|
c := a - b
|
|
return b + (c & c >> shift)
|
|
}
|
|
```
|
|
|
|
#### inline_test
|
|
```hb
|
|
fna := fn(a: uint, b: uint, c: uint): uint return a + b + c
|
|
|
|
scalar_values := fn(): uint {
|
|
return @inline(fna, 1, @inline(fna, 2, 3, 4), -10)
|
|
}
|
|
|
|
A := struct {a: uint}
|
|
AB := struct {a: A, b: uint}
|
|
|
|
mangle := fn(a: A, ab: AB): uint {
|
|
return a.a + ab.a.a - ab.b
|
|
}
|
|
|
|
structs := fn(): uint {
|
|
return @inline(mangle, .(0), .(.(@inline(mangle, .(20), .(.(5), 5))), 20))
|
|
}
|
|
|
|
main := fn(): uint {
|
|
if scalar_values() != 0 return 1
|
|
if structs() != 0 return structs()
|
|
|
|
return 0
|
|
}
|
|
```
|
|
|
|
#### inlined_generic_functions
|
|
```hb
|
|
abs := fn($Expr: type, x: Expr): Expr {
|
|
mask := x >> @bitcast(@sizeof(Expr) - 1)
|
|
return (x ^ mask) - mask
|
|
}
|
|
|
|
main := fn(): uint {
|
|
return @inline(abs, uint, -10)
|
|
}
|
|
```
|
|
|
|
#### some_generic_code
|
|
```hb
|
|
some_func := fn($Elem: type): void {
|
|
return
|
|
}
|
|
|
|
main := fn(): void {
|
|
some_func(u8)
|
|
return
|
|
}
|
|
```
|
|
|
|
#### integer_inference_issues
|
|
```hb
|
|
.{integer_range} := @use("random.hb")
|
|
main := fn(): void {
|
|
a := integer_range(0, 1000)
|
|
return
|
|
}
|
|
|
|
// in module: random.hb
|
|
integer_range := fn(min: uint, max: uint): uint {
|
|
return @eca(3, 4) % (@bitcast(max) - min + 1) + min
|
|
}
|
|
```
|
|
|
|
#### signed_to_unsigned_upcast
|
|
```hb
|
|
main := fn(): uint return @as(i32, 1)
|
|
```
|
|
|
|
#### writing_into_string
|
|
```hb
|
|
outl := fn(): void {
|
|
msg := "whahaha\0"
|
|
_u := @as(u8, 0)
|
|
return
|
|
}
|
|
|
|
inl := fn(): void {
|
|
msg := "luhahah\0"
|
|
return
|
|
}
|
|
|
|
main := fn(): void {
|
|
outl()
|
|
inl()
|
|
return
|
|
}
|
|
```
|
|
|
|
#### request_page
|
|
```hb
|
|
request_page := fn(page_count: u8): ^u8 {
|
|
msg := "\{00}\{01}xxxxxxxx\0"
|
|
msg_page_count := msg + 1;
|
|
*msg_page_count = page_count
|
|
return @eca(3, 2, msg, 12)
|
|
}
|
|
|
|
create_back_buffer := fn(total_pages: int): ^u32 {
|
|
if total_pages <= 0xFF {
|
|
return @bitcast(request_page(@intcast(total_pages)))
|
|
}
|
|
ptr := request_page(255)
|
|
remaining := total_pages - 0xFF
|
|
loop if remaining <= 0 break else {
|
|
if remaining < 0xFF {
|
|
_ = request_page(@intcast(remaining))
|
|
} else {
|
|
_ = request_page(0xFF)
|
|
}
|
|
remaining -= 0xFF
|
|
}
|
|
return @bitcast(ptr)
|
|
}
|
|
|
|
main := fn(): void {
|
|
_f := create_back_buffer(400)
|
|
return
|
|
}
|
|
```
|
|
|
|
#### tests_ptr_to_ptr_copy
|
|
```hb
|
|
main := fn(): uint {
|
|
back_buffer := @as([1024 * 10]u8, idk)
|
|
|
|
n := 0
|
|
loop if n >= 1024 break else {
|
|
back_buffer[n] = 64
|
|
n += 1
|
|
}
|
|
n = 1
|
|
loop if n >= 10 break else {
|
|
*(@as(^[1024]u8, @bitcast(&back_buffer)) + n) = *@bitcast(&back_buffer)
|
|
n += 1
|
|
}
|
|
return back_buffer[1024 * 2]
|
|
}
|
|
```
|
|
|
|
#### global_variable_wiredness
|
|
```hb
|
|
ports := false
|
|
|
|
inb := fn(): uint return 0
|
|
|
|
main := fn(): void {
|
|
if ports {
|
|
ports = inb() == 0x0
|
|
}
|
|
}
|
|
```
|
|
|
|
### Just Testing Optimizations
|
|
|
|
#### elide_stack_offsets_for_parameters_correctly
|
|
```hb
|
|
A := struct {
|
|
f: uint,
|
|
s: B,
|
|
}
|
|
|
|
B := struct {
|
|
a: uint,
|
|
b: uint,
|
|
}
|
|
|
|
main := fn(): uint {
|
|
a := A.(1, .(0, 0))
|
|
return pass(a.s)
|
|
}
|
|
|
|
pass := fn(s: B): uint return s.a + s.b
|
|
```
|
|
|
|
#### null_check_test
|
|
```hb
|
|
get_ptr := fn(): ?^uint return null
|
|
|
|
main := fn(): uint {
|
|
ptr := get_ptr()
|
|
|
|
if ptr == null {
|
|
return 0
|
|
}
|
|
|
|
loop if *ptr != 10 {
|
|
*ptr += 1
|
|
} else break
|
|
|
|
return *ptr
|
|
}
|
|
```
|
|
|
|
#### const_folding_with_arg
|
|
```hb
|
|
main := fn(arg: uint): uint {
|
|
// reduces to 0
|
|
return arg + 0 - arg * 1 + arg + 1 + arg + 2 + arg + 3 - arg * 3 - 6
|
|
}
|
|
```
|
|
|
|
#### branch_assignments
|
|
```hb
|
|
main := fn(arg: uint): uint {
|
|
if arg == 1 {
|
|
arg = 1
|
|
} else if arg == 0 {
|
|
arg = 2
|
|
} else {
|
|
arg = 3
|
|
}
|
|
return arg
|
|
}
|
|
```
|
|
|
|
#### exhaustive_loop_testing
|
|
```hb
|
|
main := fn(): uint {
|
|
loop break
|
|
|
|
x := 0
|
|
loop {
|
|
x += 1
|
|
break
|
|
}
|
|
|
|
if multiple_breaks(0) != 3 {
|
|
return true
|
|
}
|
|
|
|
if multiple_breaks(4) != 10 {
|
|
return 2
|
|
}
|
|
|
|
if state_change_in_break(0) != 0 {
|
|
return 3
|
|
}
|
|
|
|
if state_change_in_break(4) != 10 {
|
|
return 4
|
|
}
|
|
|
|
if continue_and_state_change(10) != 10 {
|
|
return 5
|
|
}
|
|
|
|
if continue_and_state_change(3) != 0 {
|
|
return 6
|
|
}
|
|
|
|
infinite_loop()
|
|
return 0
|
|
}
|
|
|
|
infinite_loop := fn(): void {
|
|
f := 0
|
|
loop {
|
|
if f == 1 {
|
|
f = 0
|
|
} else {
|
|
f = 1
|
|
}
|
|
|
|
f = continue_and_state_change(0)
|
|
}
|
|
}
|
|
|
|
multiple_breaks := fn(arg: uint): uint {
|
|
loop if arg < 10 {
|
|
arg += 1
|
|
if arg == 3 break
|
|
} else break
|
|
return arg
|
|
}
|
|
|
|
state_change_in_break := fn(arg: uint): uint {
|
|
loop if arg < 10 {
|
|
if arg == 3 {
|
|
arg = 0
|
|
break
|
|
}
|
|
arg += 1
|
|
} else break
|
|
return arg
|
|
}
|
|
|
|
continue_and_state_change := fn(arg: uint): uint {
|
|
loop if arg < 10 {
|
|
if arg == 2 {
|
|
arg = 4
|
|
continue
|
|
}
|
|
if arg == 3 {
|
|
arg = 0
|
|
break
|
|
}
|
|
arg += 1
|
|
} else break
|
|
return arg
|
|
}
|
|
```
|
|
|
|
#### pointer_opts
|
|
```hb
|
|
main := fn(): uint {
|
|
mem := &0;
|
|
*mem = 1;
|
|
*mem = 2
|
|
|
|
b := *mem + *mem
|
|
clobber(mem)
|
|
|
|
b -= *mem
|
|
return b
|
|
}
|
|
|
|
clobber := fn(cb: ^uint): void {
|
|
*cb = 4
|
|
return
|
|
}
|
|
```
|
|
|
|
#### conditional_stores
|
|
```hb
|
|
main := fn(): uint {
|
|
cnd := cond()
|
|
mem := &1
|
|
|
|
if cnd == 0 {
|
|
*mem = 0
|
|
} else {
|
|
*mem = 2
|
|
}
|
|
|
|
return *mem
|
|
}
|
|
|
|
cond := fn(): uint return 0
|
|
```
|
|
|
|
#### loop_stores
|
|
```hb
|
|
main := fn(): uint {
|
|
mem := &10
|
|
|
|
loop if *mem == 0 break else {
|
|
*mem -= 1
|
|
}
|
|
|
|
return *mem
|
|
}
|
|
```
|
|
|
|
#### dead_code_in_loop
|
|
```hb
|
|
main := fn(): uint {
|
|
n := 0
|
|
|
|
loop if n < 10 {
|
|
if n < 10 break
|
|
n += 1
|
|
} else break
|
|
|
|
loop if n == 0 return n
|
|
|
|
return 1
|
|
}
|
|
```
|
|
|
|
#### infinite_loop_after_peephole
|
|
```hb
|
|
main := fn(): uint {
|
|
n := 0
|
|
f := 0
|
|
loop if n != 0 break else {
|
|
f += 1
|
|
}
|
|
return f
|
|
}
|
|
```
|
|
|
|
#### aliasing_overoptimization
|
|
```hb
|
|
Foo := struct {ptr: ^uint, rnd: uint}
|
|
|
|
main := fn(): uint {
|
|
mem := &2
|
|
stru := Foo.(mem, 0);
|
|
*stru.ptr = 0
|
|
return *mem
|
|
}
|
|
```
|
|
|
|
#### global_aliasing_overptimization
|
|
```hb
|
|
var := 0
|
|
|
|
main := fn(): uint {
|
|
var = 2
|
|
clobber()
|
|
return var
|
|
}
|
|
|
|
clobber := fn(): void {
|
|
var = 0
|
|
}
|
|
```
|
|
|
|
#### overwrite_aliasing_overoptimization
|
|
```hb
|
|
Foo := struct {a: int, b: int}
|
|
Bar := struct {f: Foo, b: int}
|
|
|
|
main := fn(): int {
|
|
value := Bar.{b: 1, f: .(4, 1)}
|
|
value.f = opaque()
|
|
return value.f.a - value.f.b - value.b
|
|
}
|
|
|
|
opaque := fn(): Foo {
|
|
return .(3, 2)
|
|
}
|
|
```
|
|
|
|
#### more_if_opts
|
|
```hb
|
|
main := fn(): uint {
|
|
opq1 := opaque()
|
|
opq2 := opaque()
|
|
a := 0
|
|
|
|
if opq1 == null {
|
|
} else a = *opq1
|
|
if opq1 != null a = *opq1
|
|
//if opq1 == null | opq2 == null {
|
|
//} else a = *opq1
|
|
//if opq1 != null & opq2 != null a = *opq1
|
|
|
|
return a
|
|
}
|
|
|
|
opaque := fn(): ?^uint return null
|
|
```
|