unroll macro

This commit is contained in:
Judah Caruso 2025-05-19 16:56:53 -06:00
parent 2b42519e30
commit e6b0fb7cdf
3 changed files with 157 additions and 16 deletions

View file

@ -7,8 +7,8 @@
#import,file "./hash/module.jai"(true); #import,file "./hash/module.jai"(true);
rmath :: #import,file "./math/module.jai"(.radians, true); rmath :: #import,file "./math/module.jai"(.radians, true);
dmath :: #import,file "./math/module.jai"(.degrees, true); // dmath :: #import,file "./math/module.jai"(.degrees, true);
tmath :: #import,file "./math/module.jai"(.turns, true); // tmath :: #import,file "./math/module.jai"(.turns, true);
} }

View file

@ -71,25 +71,49 @@ operator | :: with;
/* /*
Creates a named block that can exit early (via 'break' or 'continue'). Creates a named block that can exit early (via 'break' or 'continue').
This mostly replaces the case where you'd like to jump to This mostly replaces the case where you'd like to jump to
the end of a scope based on some logic within. Without the end of a scope based on some logic within. Without
gotos, this is the next best thing. gotos, this is the next best thing.
Usage: Usage:
// within a loop // within a loop
for this_block() { // this is named 'block' by default for this_block() { // this is named 'block' by default
if !moving break; if !moving break;
// do movement here // do movement here
} }
for this_block("render_player") { for this_block("render_player") {
if invisible break render_player; if invisible break render_player;
// do rendering here // do rendering here
} }
*/ */
this_block :: ($name: string = Default_Name) -> Named_Block(name) #expand { return .{}; } this_block :: ($name: string = Default_Name) -> Named_Block(name) #expand { return .{}; }
/*
Drop-in loop unrolling macro.
Usage:
for unroll(5) {
// duplicates this body 5 times exactly
}
known_size: [3]float;
for unroll(known_size) {
// duplicates this body 3 times exactly
}
var_size: []float;
for unroll(var_size) {
// duplicates this body a set number of times,
// falling back to a regular for loop to handle
// the remaining iterations.
}
*/
unroll :: ($count: int) -> Unrolled_Loop(count) { return .{}; }
unroll :: (arr: [$N]$T) -> Unrolled_Loop(N, T) { return .{ array = arr }; }
unroll :: (arr: []$T) -> Unrolled_Loop(-1, T) { return .{ array = arr }; }
// Call #c_call procedures inline with the current context: 'c_call(some_c_call_proc(10, 20))' // Call #c_call procedures inline with the current context: 'c_call(some_c_call_proc(10, 20))'
c_call :: (call: Code) #expand { c_call :: (call: Code) #expand {
push_context context { #insert,scope(call) call; } push_context context { #insert,scope(call) call; }
@ -118,6 +142,104 @@ for_expansion :: (v: *Named_Block, code: Code, _: For_Flags) #expand {
ifx v.NAME.count != 0 v.NAME else Default_Name); ifx v.NAME.count != 0 v.NAME else Default_Name);
} }
Unrolled_Loop :: struct(N: int, T: Type = void) {
// Only store arrays when we absolutely have to.
#if T != void {
// @todo(judah): because this will only be created via 'unroll',
// should these be pointers to the underlying arrays so we don't
// pay for a copy?
#if N == -1 {
array: []T = ---;
}
else {
array: [N]T = ---;
}
}
}
for_expansion :: (loop: *Unrolled_Loop, body: Code, flags: For_Flags, loc := #caller_location) #expand {
#assert flags & .REVERSE == 0 "reverse iteration not supported with loop unrolling (for now)";
#assert flags & .POINTER == 0 "pointer iteration not supported with loop unrolling (for now)";
// runtime unroll
#if loop.N == -1 {
for i: 0..loop.array.count - 1 {
`it := #no_abc loop.array[i];
`it_index := i;
#insert,scope(body) body;
}
// @todo(judah): below doesn't properly handle counts not divisible by 4,
// so we end up going over the bounds of the array.
// UNROLL_AMOUNT :: 4; // @todo(judah): make this configurable?
// unrolled_loops := loop.array.count / UNROLL_AMOUNT;
// remainder_loops := loop.array.count % UNROLL_AMOUNT;
// `it: loop.T;
// for i: 0..unrolled_loops #no_abc {
// basic.print("I: %\n", i);
// index := i * UNROLL_AMOUNT;
// it_index = index + 0;
// it = loop.array[it_index];
// #insert,scope(body) body;
// it_index = index + 1;
// it = loop.array[it_index];
// #insert,scope(body) body;
// it_index = index + 2;
// it = loop.array[it_index];
// #insert,scope(body) body;
// it_index = index + 3;
// it = loop.array[it_index];
// #insert,scope(body) body;
// }
// after_big_loop := it_index;
// for i: 0..remainder_loops #no_abc {
// it_index = after_big_loop + i;
// it = loop.array[it_index];
// #insert,scope(body) body;
// }
}
// compile-time unroll
else {
`it_index := 0;
#insert -> string {
b: basic.String_Builder;
basic.print_to_builder(*b, "// inserted unrolled loop (N = %) at %:%\n", loop.N, loc.fully_pathed_filename, loc.line_number);
if loop.T == void {
basic.append(*b, "`it: int = ---;\n");
}
else {
basic.append(*b, "`it: loop.T = ---;\n");
}
for 0..loop.N - 1 {
basic.append(*b, "{\n");
if loop.T == void {
basic.print_to_builder(*b, "\tit = %;\n", it);
}
else {
basic.print_to_builder(*b, "\tit = #no_abc loop.array[%];\n", it);
}
basic.print_to_builder(*b, "\tit_index = %;\n", it);
basic.append(*b, "\t#insert,scope(body) body;\n");
basic.append(*b, "}\n");
}
return basic.builder_to_string(*b);
}
}
}
pp :: #import "Program_Print"; pp :: #import "Program_Print";
compiler :: #import "Compiler"; compiler :: #import "Compiler";

View file

@ -97,24 +97,28 @@ for_expansion :: (v: *Vec, body: Code, flags: For_Flags) #expand {
operator + :: inline (l: Vec, r: Vec(l.N, l.T)) -> Vec(l.N, l.T) #no_abc { operator + :: inline (l: Vec, r: Vec(l.N, l.T)) -> Vec(l.N, l.T) #no_abc {
res: Vec(l.N, l.T) = ---; res: Vec(l.N, l.T) = ---;
// @todo(judah): unroll for N <= 4
for l res[it_index] = it + r[it_index]; for l res[it_index] = it + r[it_index];
return res; return res;
} }
operator - :: inline (l: Vec, r: Vec(l.N, l.T)) -> Vec(l.N, l.T) #no_abc { operator - :: inline (l: Vec, r: Vec(l.N, l.T)) -> Vec(l.N, l.T) #no_abc {
res: Vec(l.N, l.T) = ---; res: Vec(l.N, l.T) = ---;
// @todo(judah): unroll for N <= 4
for l res[it_index] = it - r[it_index]; for l res[it_index] = it - r[it_index];
return res; return res;
} }
operator * :: inline (l: Vec, r: Vec(l.N, l.T)) -> Vec(l.N, l.T) #no_abc { operator * :: inline (l: Vec, r: Vec(l.N, l.T)) -> Vec(l.N, l.T) #no_abc {
res: Vec(l.N, l.T) = ---; res: Vec(l.N, l.T) = ---;
// @todo(judah): unroll for N <= 4
for l res[it_index] = it * r[it_index]; for l res[it_index] = it * r[it_index];
return res; return res;
} }
operator / :: inline (l: Vec, r: Vec(l.N, l.T)) -> Vec(l.N, l.T) #no_abc { operator / :: inline (l: Vec, r: Vec(l.N, l.T)) -> Vec(l.N, l.T) #no_abc {
res: Vec(l.N, l.T) = ---; res: Vec(l.N, l.T) = ---;
// @todo(judah): unroll for N <= 4
for l res[it_index] = it / r[it_index]; for l res[it_index] = it / r[it_index];
return res; return res;
} }
@ -165,3 +169,18 @@ v4i :: (x: $T = 0, y: T = 0, z: T = 0, w: T = 0) -> Vec(4, T)
quat :: (x: float = 0, y: float = 0, z: float = 0, w: float = 0) -> Quat #expand { quat :: (x: float = 0, y: float = 0, z: float = 0, w: float = 0) -> Quat #expand {
return .{ x = x, y = y, z = z, w = w }; return .{ x = x, y = y, z = z, w = w };
} }
#if RUN_TESTS #run,stallable {
test.run("vec2:ops", t => {
a := v2f(10.0, 1);
b := v2f(20.0, 2);
c := a + b;
test.expect(t, c.x == 30 && c.y == 3);
});
}
#scope_file;
jx :: #import,file "../module.jai";