authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-01-26 19:47:15+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-01-26 19:47:15+01:00
logcc46c1b9024beefdd82ce8abd07e8849a72db20c
tree5771573eccca912cd46fa1fb090cf57b71913989
parenta0d81caec99fe0d1cd803b0ba461b6e02829b476
signaturelock-open Commit is signed but in an unrecognized format.

Add tests, fix locals that are created in blocks like loops, and handle all breaks correctly


3 files changed, 146 insertions(+), 41 deletions(-)

src/codegen/wasm.zig+46-40
...@@ -163,11 +163,8 @@ pub const Context = struct {...@@ -163,11 +163,8 @@ pub const Context = struct {
163 try self.genFunctype();163 try self.genFunctype();
164 const writer = self.code.writer();164 const writer = self.code.writer();
165165
166 // Reserve space to write the size after generating the code166 // Reserve space to write the size after generating the code as well as space for locals count
167 try self.code.resize(5);167 try self.code.resize(10);
168
169 // offset into 'code' section where we will put our locals count
170 var local_offset = self.code.items.len;
171168
172 // Write instructions169 // Write instructions
173 // TODO: check for and handle death of instructions170 // TODO: check for and handle death of instructions
...@@ -177,10 +174,10 @@ pub const Context = struct {...@@ -177,10 +174,10 @@ pub const Context = struct {
177174
178 // finally, write our local types at the 'offset' position175 // finally, write our local types at the 'offset' position
179 {176 {
180 var totals_buffer: [5]u8 = undefined;177 leb.writeUnsignedFixed(5, self.code.items[5..10], @intCast(u32, self.locals.items.len));
181 leb.writeUnsignedFixed(5, totals_buffer[0..5], @intCast(u32, self.locals.items.len));178
182 try self.code.insertSlice(local_offset, &totals_buffer);179 // offset into 'code' section where we will put our locals types
183 local_offset += 5;180 var local_offset: usize = 10;
184181
185 // emit the actual locals amount182 // emit the actual locals amount
186 for (self.locals.items) |local| {183 for (self.locals.items) |local| {
...@@ -285,8 +282,7 @@ pub const Context = struct {...@@ -285,8 +282,7 @@ pub const Context = struct {
285 }282 }
286283
287 fn genLoad(self: *Context, inst: *Inst.UnOp) InnerError!WValue {284 fn genLoad(self: *Context, inst: *Inst.UnOp) InnerError!WValue {
288 const operand = self.resolveInst(inst.operand);285 return self.resolveInst(inst.operand);
289 return operand;
290 }286 }
291287
292 fn genArg(self: *Context, inst: *Inst.Arg) InnerError!WValue {288 fn genArg(self: *Context, inst: *Inst.Arg) InnerError!WValue {
...@@ -351,35 +347,49 @@ pub const Context = struct {...@@ -351,35 +347,49 @@ pub const Context = struct {
351 fn genBlock(self: *Context, block: *Inst.Block) InnerError!WValue {347 fn genBlock(self: *Context, block: *Inst.Block) InnerError!WValue {
352 const block_ty = try self.genBlockType(block.base.src, block.base.ty);348 const block_ty = try self.genBlockType(block.base.src, block.base.ty);
353349
350 try self.startBlock(.block, block_ty, null);
354 block.codegen = .{351 block.codegen = .{
355 // we don't use relocs, so using `relocs` is illegal behaviour.352 // we don't use relocs, so using `relocs` is illegal behaviour.
356 .relocs = undefined,353 .relocs = undefined,
357 // Here we set the current block idx, so conditions know the depth to jump354 // Here we set the current block idx, so breaks know the depth to jump
358 // to when breaking out. This will be set to .none when it is found again within355 // to when breaking out.
359 // the same block
360 .mcv = @bitCast(AnyMCValue, WValue{ .block_idx = self.block_depth }),356 .mcv = @bitCast(AnyMCValue, WValue{ .block_idx = self.block_depth }),
361 };357 };
358 try self.genBody(block.body);
359 try self.endBlock();
360
361 return .none;
362 }
363
364 /// appends a new wasm block to the code section and increases the `block_depth` by 1
365 fn startBlock(self: *Context, block_type: wasm.Opcode, valtype: u8, with_offset: ?usize) !void {
362 self.block_depth += 1;366 self.block_depth += 1;
367 if (with_offset) |offset| {
368 try self.code.insert(offset, wasm.opcode(block_type));
369 try self.code.insert(offset + 1, valtype);
370 } else {
371 try self.code.append(wasm.opcode(block_type));
372 try self.code.append(valtype);
373 }
374 }
363375
364 try self.code.append(wasm.opcode(.block));376 /// Ends the current wasm block and decreases the `block_depth` by 1
365 try self.code.append(block_ty);377 fn endBlock(self: *Context) !void {
366 try self.genBody(block.body);
367 try self.code.append(wasm.opcode(.end));378 try self.code.append(wasm.opcode(.end));
368
369 self.block_depth -= 1;379 self.block_depth -= 1;
370 return .none;
371 }380 }
372381
373 fn genLoop(self: *Context, loop: *Inst.Loop) InnerError!WValue {382 fn genLoop(self: *Context, loop: *Inst.Loop) InnerError!WValue {
374 const loop_ty = try self.genBlockType(loop.base.src, loop.base.ty);383 const loop_ty = try self.genBlockType(loop.base.src, loop.base.ty);
375384
376 try self.code.append(wasm.opcode(.loop));385 try self.startBlock(.loop, loop_ty, null);
377 try self.code.append(loop_ty);
378 self.block_depth += 1;
379 try self.genBody(loop.body);386 try self.genBody(loop.body);
380 self.block_depth -= 1;
381387
382 try self.code.append(wasm.opcode(.end));388 // breaking to the index of a loop block will continue the loop instead
389 try self.code.append(wasm.opcode(.br));
390 try leb.writeULEB128(self.code.writer(), @as(u32, 0));
391
392 try self.endBlock();
383393
384 return .none;394 return .none;
385 }395 }
...@@ -388,23 +398,22 @@ pub const Context = struct {...@@ -388,23 +398,22 @@ pub const Context = struct {
388 const condition = self.resolveInst(condbr.condition);398 const condition = self.resolveInst(condbr.condition);
389 const writer = self.code.writer();399 const writer = self.code.writer();
390400
401 // TODO: Handle death instructions for then and else body
402
391 // insert blocks at the position of `offset` so403 // insert blocks at the position of `offset` so
392 // the condition can jump to it404 // the condition can jump to it
393 const offset = condition.code_offset;405 const offset = condition.code_offset;
394 try self.code.insert(offset, wasm.opcode(.block));406 const block_ty = try self.genBlockType(condbr.base.src, condbr.base.ty);
395 try self.code.insert(offset, try self.genBlockType(condbr.base.src, condbr.base.ty));407 try self.startBlock(.block, block_ty, offset);
396408
397 // we inserted the block in front of the condition409 // we inserted the block in front of the condition
398 // so now check if condition matches. If not, break outside this block410 // so now check if condition matches. If not, break outside this block
399 // and continue with the regular codepath411 // and continue with the then codepath
400 try writer.writeByte(wasm.opcode(.br_if));412 try writer.writeByte(wasm.opcode(.br_if));
401 try leb.writeULEB128(writer, @as(u32, 0));413 try leb.writeULEB128(writer, @as(u32, 0));
402414
403 // else body in case condition does not match
404 try self.genBody(condbr.else_body);415 try self.genBody(condbr.else_body);
405416 try self.endBlock();
406 // finally, tell wasm we have reached the end of the block we inserted above
407 try writer.writeByte(wasm.opcode(.end));
408417
409 // Outer block that matches the condition418 // Outer block that matches the condition
410 try self.genBody(condbr.then_body);419 try self.genBody(condbr.then_body);
...@@ -417,7 +426,7 @@ pub const Context = struct {...@@ -417,7 +426,7 @@ pub const Context = struct {
417426
418 // save offset, so potential conditions can insert blocks in front of427 // save offset, so potential conditions can insert blocks in front of
419 // the comparison that we can later jump back to428 // the comparison that we can later jump back to
420 const offset = self.code.items.len - 1;429 const offset = self.code.items.len;
421430
422 const lhs = self.resolveInst(inst.lhs);431 const lhs = self.resolveInst(inst.lhs);
423 const rhs = self.resolveInst(inst.rhs);432 const rhs = self.resolveInst(inst.rhs);
...@@ -492,17 +501,14 @@ pub const Context = struct {...@@ -492,17 +501,14 @@ pub const Context = struct {
492 try self.emitWValue(operand);501 try self.emitWValue(operand);
493 }502 }
494503
495 // if the block contains a block_idx, do a relative jump to it504 // every block contains a `WValue` with its block index.
496 // if `wvalue` was already 'consumed', simply break out of current block505 // We then determine how far we have to jump to it by substracting it from current block depth
497 const wvalue = @bitCast(WValue, br.block.codegen.mcv);506 const wvalue = @bitCast(WValue, br.block.codegen.mcv);
498 const idx: u32 = if (wvalue == .block_idx) blk: {507 const idx: u32 = self.block_depth - wvalue.block_idx;
499 br.block.codegen.mcv = @bitCast(AnyMCValue, WValue{ .none = {} });
500 break :blk self.block_depth - wvalue.block_idx;
501 } else 0;
502
503 const writer = self.code.writer();508 const writer = self.code.writer();
504 try writer.writeByte(wasm.opcode(.br));509 try writer.writeByte(wasm.opcode(.br));
505 try leb.writeULEB128(writer, idx);510 try leb.writeULEB128(writer, idx);
506 return WValue.none;511
512 return .none;
507 }513 }
508};514};
src/link/Wasm.zig+8-1
...@@ -122,6 +122,13 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {...@@ -122,6 +122,13 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
122 else => |e| return err,122 else => |e| return err,
123 };123 };
124124
125 // as locals are patched afterwards, the offsets of funcidx's are off,
126 // here we update them to correct them
127 for (decl.fn_link.wasm.?.idx_refs.items) |*func| {
128 // For each local, add 6 bytes (count + type)
129 func.offset += @intCast(u32, context.locals.items.len * 6);
130 }
131
125 fn_data.functype = context.func_type_data.toUnmanaged();132 fn_data.functype = context.func_type_data.toUnmanaged();
126 fn_data.code = context.code.toUnmanaged();133 fn_data.code = context.code.toUnmanaged();
127}134}
...@@ -238,7 +245,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -238,7 +245,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
238 try writer.writeAll(fn_data.code.items[current..idx_ref.offset]);245 try writer.writeAll(fn_data.code.items[current..idx_ref.offset]);
239 current = idx_ref.offset;246 current = idx_ref.offset;
240 // Use a fixed width here to make calculating the code size247 // Use a fixed width here to make calculating the code size
241 // in codegen.wasm.genCode() simpler.248 // in codegen.wasm.gen() simpler.
242 var buf: [5]u8 = undefined;249 var buf: [5]u8 = undefined;
243 leb.writeUnsignedFixed(5, &buf, self.getFuncidx(idx_ref.decl).?);250 leb.writeUnsignedFixed(5, &buf, self.getFuncidx(idx_ref.decl).?);
244 try writer.writeAll(&buf);251 try writer.writeAll(&buf);
test/stage2/wasm.zig+92
...@@ -122,4 +122,96 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -122,4 +122,96 @@ pub fn addCases(ctx: *TestContext) !void {
122 \\}122 \\}
123 , "35\n");123 , "35\n");
124 }124 }
125
126 {
127 var case = ctx.exe("wasm conditions", wasi);
128
129 case.addCompareOutput(
130 \\export fn _start() u32 {
131 \\ var i: u32 = 5;
132 \\ if (i > @as(u32, 4)) {
133 \\ i += 10;
134 \\ }
135 \\ return i;
136 \\}
137 , "15\n");
138
139 case.addCompareOutput(
140 \\export fn _start() u32 {
141 \\ var i: u32 = 5;
142 \\ if (i < @as(u32, 4)) {
143 \\ i += 10;
144 \\ } else {
145 \\ i = 2;
146 \\ }
147 \\ return i;
148 \\}
149 , "2\n");
150
151 case.addCompareOutput(
152 \\export fn _start() u32 {
153 \\ var i: u32 = 5;
154 \\ if (i < @as(u32, 4)) {
155 \\ i += 10;
156 \\ } else if(i == @as(u32, 5)) {
157 \\ i = 20;
158 \\ }
159 \\ return i;
160 \\}
161 , "20\n");
162
163 case.addCompareOutput(
164 \\export fn _start() u32 {
165 \\ var i: u32 = 11;
166 \\ if (i < @as(u32, 4)) {
167 \\ i += 10;
168 \\ } else {
169 \\ if (i > @as(u32, 10)) {
170 \\ i += 20;
171 \\ } else {
172 \\ i = 20;
173 \\ }
174 \\ }
175 \\ return i;
176 \\}
177 , "31\n");
178 }
179
180 {
181 var case = ctx.exe("wasm while loops", wasi);
182
183 case.addCompareOutput(
184 \\export fn _start() u32 {
185 \\ var i: u32 = 0;
186 \\ while(i < @as(u32, 5)){
187 \\ i += 1;
188 \\ }
189 \\
190 \\ return i;
191 \\}
192 , "5\n");
193
194 case.addCompareOutput(
195 \\export fn _start() u32 {
196 \\ var i: u32 = 0;
197 \\ while(i < @as(u32, 10)){
198 \\ var x: u32 = 1;
199 \\ i += x;
200 \\ }
201 \\ return i;
202 \\}
203 , "10\n");
204
205 case.addCompareOutput(
206 \\export fn _start() u32 {
207 \\ var i: u32 = 0;
208 \\ while(i < @as(u32, 10)){
209 \\ var x: u32 = 1;
210 \\ i += x;
211 \\ if (i == @as(u32, 5)) break;
212 \\ }
213 \\ return i;
214 \\}
215 , "5\n");
216 }
125}217}