authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-01-24 14:35:14+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-01-24 14:38:35+01:00
loga0d81caec99fe0d1cd803b0ba461b6e02829b476
tree801e8c6ecc86426a98dd59dac2dfbfe3ca21cff2
parentccef167e9d5b3a34151cac46e39040b53f12eb68
signature Commit is signed but in an unrecognized format.

Nested conditions and loops support


2 files changed, 263 insertions(+), 69 deletions(-)

src/codegen/wasm.zig+260-67
...@@ -4,6 +4,7 @@ const ArrayList = std.ArrayList;...@@ -4,6 +4,7 @@ const ArrayList = std.ArrayList;
4const assert = std.debug.assert;4const assert = std.debug.assert;
5const leb = std.leb;5const leb = std.leb;
6const mem = std.mem;6const mem = std.mem;
7const wasm = std.wasm;
78
8const Module = @import("../Module.zig");9const Module = @import("../Module.zig");
9const Decl = Module.Decl;10const Decl = Module.Decl;
...@@ -12,6 +13,7 @@ const Inst = ir.Inst;...@@ -12,6 +13,7 @@ const Inst = ir.Inst;
12const Type = @import("../type.zig").Type;13const Type = @import("../type.zig").Type;
13const Value = @import("../value.zig").Value;14const Value = @import("../value.zig").Value;
14const Compilation = @import("../Compilation.zig");15const Compilation = @import("../Compilation.zig");
16const AnyMCValue = @import("../codegen.zig").AnyMCValue;
1517
16/// Wasm Value, created when generating an instruction18/// Wasm Value, created when generating an instruction
17const WValue = union(enum) {19const WValue = union(enum) {
...@@ -20,23 +22,14 @@ const WValue = union(enum) {...@@ -20,23 +22,14 @@ const WValue = union(enum) {
20 local: u32,22 local: u32,
21 /// Instruction holding a constant `Value`23 /// Instruction holding a constant `Value`
22 constant: *Inst,24 constant: *Inst,
23 /// Block label25 /// Offset position in the list of bytecode instructions
26 code_offset: usize,
27 /// The label of the block, used by breaks to find its relative distance
24 block_idx: u32,28 block_idx: u32,
25};29};
2630
27/// Hashmap to store generated `WValue` for each `Inst`31/// Hashmap to store generated `WValue` for each `Inst`
28pub const ValueTable = std.AutoHashMap(*Inst, WValue);32pub const ValueTable = std.AutoHashMapUnmanaged(*Inst, WValue);
29
30/// Using a given `Type`, returns the corresponding wasm value type
31fn genValtype(ty: Type) ?u8 {
32 return switch (ty.tag()) {
33 .f32 => 0x7D,
34 .f64 => 0x7C,
35 .u32, .i32 => 0x7F,
36 .u64, .i64 => 0x7E,
37 else => null,
38 };
39}
4033
41/// Code represents the `Code` section of wasm that34/// Code represents the `Code` section of wasm that
42/// belongs to a function35/// belongs to a function
...@@ -58,13 +51,25 @@ pub const Context = struct {...@@ -58,13 +51,25 @@ pub const Context = struct {
58 local_index: u32 = 0,51 local_index: u32 = 0,
59 /// If codegen fails, an error messages will be allocated and saved in `err_msg`52 /// If codegen fails, an error messages will be allocated and saved in `err_msg`
60 err_msg: *Module.ErrorMsg,53 err_msg: *Module.ErrorMsg,
54 /// Current block depth. Used to calculate the relative difference between a break
55 /// and block
56 block_depth: u32 = 0,
57 /// List of all locals' types generated throughout this declaration
58 /// used to emit locals count at start of 'code' section.
59 locals: std.ArrayListUnmanaged(u8),
6160
62 const InnerError = error{61 const InnerError = error{
63 OutOfMemory,62 OutOfMemory,
64 CodegenFail,63 CodegenFail,
65 };64 };
6665
67 /// Sets `err_msg` on `Context` and returns `error.CodegenFail` which is caught in link/Wasm.zig66 pub fn deinit(self: *Context) void {
67 self.values.deinit(self.gpa);
68 self.locals.deinit(self.gpa);
69 self.* = undefined;
70 }
71
72 /// Sets `err_msg` on `Context` and returns `error.CodegemFail` which is caught in link/Wasm.zig
68 fn fail(self: *Context, src: usize, comptime fmt: []const u8, args: anytype) InnerError {73 fn fail(self: *Context, src: usize, comptime fmt: []const u8, args: anytype) InnerError {
69 self.err_msg = try Module.ErrorMsg.create(self.gpa, .{74 self.err_msg = try Module.ErrorMsg.create(self.gpa, .{
70 .file_scope = self.decl.getFileScope(),75 .file_scope = self.decl.getFileScope(),
...@@ -85,13 +90,35 @@ pub const Context = struct {...@@ -85,13 +90,35 @@ pub const Context = struct {
85 return self.values.get(inst).?; // Instruction does not dominate all uses!90 return self.values.get(inst).?; // Instruction does not dominate all uses!
86 }91 }
8792
93 /// Using a given `Type`, returns the corresponding wasm value type
94 fn genValtype(self: *Context, src: usize, ty: Type) InnerError!u8 {
95 return switch (ty.tag()) {
96 .f32 => wasm.valtype(.f32),
97 .f64 => wasm.valtype(.f64),
98 .u32, .i32 => wasm.valtype(.i32),
99 .u64, .i64 => wasm.valtype(.i64),
100 else => self.fail(src, "TODO - Wasm genValtype for type '{s}'", .{ty.tag()}),
101 };
102 }
103
104 /// Using a given `Type`, returns the corresponding wasm value type
105 /// Differently from `genValtype` this also allows `void` to create a block
106 /// with no return type
107 fn genBlockType(self: *Context, src: usize, ty: Type) InnerError!u8 {
108 return switch (ty.tag()) {
109 .void, .noreturn => wasm.block_empty,
110 else => self.genValtype(src, ty),
111 };
112 }
113
88 /// Writes the bytecode depending on the given `WValue` in `val`114 /// Writes the bytecode depending on the given `WValue` in `val`
89 fn emitWValue(self: *Context, val: WValue) InnerError!void {115 fn emitWValue(self: *Context, val: WValue) InnerError!void {
90 const writer = self.code.writer();116 const writer = self.code.writer();
91 switch (val) {117 switch (val) {
92 .none, .block_idx => {},118 .block_idx => unreachable,
119 .none, .code_offset => {},
93 .local => |idx| {120 .local => |idx| {
94 try writer.writeByte(0x20); // local.get121 try writer.writeByte(wasm.opcode(.local_get));
95 try leb.writeULEB128(writer, idx);122 try leb.writeULEB128(writer, idx);
96 },123 },
97 .constant => |inst| try self.emitConstant(inst.castTag(.constant).?), // creates a new constant onto the stack124 .constant => |inst| try self.emitConstant(inst.castTag(.constant).?), // creates a new constant onto the stack
...@@ -102,8 +129,7 @@ pub const Context = struct {...@@ -102,8 +129,7 @@ pub const Context = struct {
102 const ty = self.decl.typed_value.most_recent.typed_value.ty;129 const ty = self.decl.typed_value.most_recent.typed_value.ty;
103 const writer = self.func_type_data.writer();130 const writer = self.func_type_data.writer();
104131
105 // functype magic132 try writer.writeByte(wasm.function_type);
106 try writer.writeByte(0x60);
107133
108 // param types134 // param types
109 try leb.writeULEB128(writer, @intCast(u32, ty.fnParamLen()));135 try leb.writeULEB128(writer, @intCast(u32, ty.fnParamLen()));
...@@ -112,8 +138,8 @@ pub const Context = struct {...@@ -112,8 +138,8 @@ pub const Context = struct {
112 defer self.gpa.free(params);138 defer self.gpa.free(params);
113 ty.fnParamTypes(params);139 ty.fnParamTypes(params);
114 for (params) |param_type| {140 for (params) |param_type| {
115 const val_type = genValtype(param_type) orelse141 // Can we maybe get the source index of each param?
116 return self.fail(self.decl.src(), "TODO: Wasm codegen - arg type value for type '{s}'", .{param_type.tag()});142 const val_type = try self.genValtype(self.decl.src(), param_type);
117 try writer.writeByte(val_type);143 try writer.writeByte(val_type);
118 }144 }
119 }145 }
...@@ -124,8 +150,8 @@ pub const Context = struct {...@@ -124,8 +150,8 @@ pub const Context = struct {
124 .void, .noreturn => try leb.writeULEB128(writer, @as(u32, 0)),150 .void, .noreturn => try leb.writeULEB128(writer, @as(u32, 0)),
125 else => |ret_type| {151 else => |ret_type| {
126 try leb.writeULEB128(writer, @as(u32, 1));152 try leb.writeULEB128(writer, @as(u32, 1));
127 const val_type = genValtype(return_type) orelse153 // Can we maybe get the source index of the return type?
128 return self.fail(self.decl.src(), "TODO: Wasm codegen - return type value for type '{s}'", .{ret_type});154 const val_type = try self.genValtype(self.decl.src(), return_type);
129 try writer.writeByte(val_type);155 try writer.writeByte(val_type);
130 },156 },
131 }157 }
...@@ -140,37 +166,33 @@ pub const Context = struct {...@@ -140,37 +166,33 @@ pub const Context = struct {
140 // Reserve space to write the size after generating the code166 // Reserve space to write the size after generating the code
141 try self.code.resize(5);167 try self.code.resize(5);
142168
169 // offset into 'code' section where we will put our locals count
170 var local_offset = self.code.items.len;
171
143 // Write instructions172 // Write instructions
144 // TODO: check for and handle death of instructions173 // TODO: check for and handle death of instructions
145 const tv = self.decl.typed_value.most_recent.typed_value;174 const tv = self.decl.typed_value.most_recent.typed_value;
146 const mod_fn = tv.val.castTag(.function).?.data;175 const mod_fn = tv.val.castTag(.function).?.data;
176 try self.genBody(mod_fn.body);
147177
148 var locals = std.ArrayList(u8).init(self.gpa);178 // finally, write our local types at the 'offset' position
149 defer locals.deinit();179 {
150180 var totals_buffer: [5]u8 = undefined;
151 for (mod_fn.body.instructions) |inst| {181 leb.writeUnsignedFixed(5, totals_buffer[0..5], @intCast(u32, self.locals.items.len));
152 if (inst.tag != .alloc) continue;182 try self.code.insertSlice(local_offset, &totals_buffer);
153183 local_offset += 5;
154 const alloc: *Inst.NoOp = inst.castTag(.alloc).?;184
155 const elem_type = alloc.base.ty.elemType();185 // emit the actual locals amount
156186 for (self.locals.items) |local| {
157 const wasm_type = genValtype(elem_type) orelse187 var buf: [6]u8 = undefined;
158 return self.fail(inst.src, "TODO: Wasm codegen - valtype for type '{s}'", .{elem_type.tag()});188 leb.writeUnsignedFixed(5, buf[0..5], @as(u32, 1));
159189 buf[5] = local;
160 try locals.append(wasm_type);190 try self.code.insertSlice(local_offset, &buf);
161 }191 local_offset += 6;
162192 }
163 try leb.writeULEB128(writer, @intCast(u32, locals.items.len));
164
165 // emit the actual locals amount
166 for (locals.items) |local| {
167 try leb.writeULEB128(writer, @as(u32, 1));
168 try leb.writeULEB128(writer, local); // valtype
169 }193 }
170194
171 try self.genBody(mod_fn.body);195 try writer.writeByte(wasm.opcode(.end));
172
173 try writer.writeByte(0x0B); // end
174196
175 // Fill in the size of the generated code to the reserved space at the197 // Fill in the size of the generated code to the reserved space at the
176 // beginning of the buffer.198 // beginning of the buffer.
...@@ -183,10 +205,20 @@ pub const Context = struct {...@@ -183,10 +205,20 @@ pub const Context = struct {
183 .add => self.genAdd(inst.castTag(.add).?),205 .add => self.genAdd(inst.castTag(.add).?),
184 .alloc => self.genAlloc(inst.castTag(.alloc).?),206 .alloc => self.genAlloc(inst.castTag(.alloc).?),
185 .arg => self.genArg(inst.castTag(.arg).?),207 .arg => self.genArg(inst.castTag(.arg).?),
208 .block => self.genBlock(inst.castTag(.block).?),
209 .br => self.genBr(inst.castTag(.br).?),
186 .call => self.genCall(inst.castTag(.call).?),210 .call => self.genCall(inst.castTag(.call).?),
211 .cmp_eq => self.genCmp(inst.castTag(.cmp_eq).?, .eq),
212 .cmp_gte => self.genCmp(inst.castTag(.cmp_gte).?, .gte),
213 .cmp_gt => self.genCmp(inst.castTag(.cmp_gt).?, .gt),
214 .cmp_lte => self.genCmp(inst.castTag(.cmp_lte).?, .lte),
215 .cmp_lt => self.genCmp(inst.castTag(.cmp_lt).?, .lt),
216 .cmp_neq => self.genCmp(inst.castTag(.cmp_neq).?, .neq),
217 .condbr => self.genCondBr(inst.castTag(.condbr).?),
187 .constant => unreachable,218 .constant => unreachable,
188 .dbg_stmt => WValue.none,219 .dbg_stmt => WValue.none,
189 .load => self.genLoad(inst.castTag(.load).?),220 .load => self.genLoad(inst.castTag(.load).?),
221 .loop => self.genLoop(inst.castTag(.loop).?),
190 .ret => self.genRet(inst.castTag(.ret).?),222 .ret => self.genRet(inst.castTag(.ret).?),
191 .retvoid => WValue.none,223 .retvoid => WValue.none,
192 .store => self.genStore(inst.castTag(.store).?),224 .store => self.genStore(inst.castTag(.store).?),
...@@ -197,7 +229,7 @@ pub const Context = struct {...@@ -197,7 +229,7 @@ pub const Context = struct {
197 fn genBody(self: *Context, body: ir.Body) InnerError!void {229 fn genBody(self: *Context, body: ir.Body) InnerError!void {
198 for (body.instructions) |inst| {230 for (body.instructions) |inst| {
199 const result = try self.genInst(inst);231 const result = try self.genInst(inst);
200 try self.values.putNoClobber(inst, result);232 try self.values.putNoClobber(self.gpa, inst, result);
201 }233 }
202 }234 }
203235
...@@ -205,7 +237,7 @@ pub const Context = struct {...@@ -205,7 +237,7 @@ pub const Context = struct {
205 // TODO: Implement tail calls237 // TODO: Implement tail calls
206 const operand = self.resolveInst(inst.operand);238 const operand = self.resolveInst(inst.operand);
207 try self.emitWValue(operand);239 try self.emitWValue(operand);
208 return WValue.none;240 return .none;
209 }241 }
210242
211 fn genCall(self: *Context, inst: *Inst.Call) InnerError!WValue {243 fn genCall(self: *Context, inst: *Inst.Call) InnerError!WValue {
...@@ -219,7 +251,7 @@ pub const Context = struct {...@@ -219,7 +251,7 @@ pub const Context = struct {
219 try self.emitWValue(arg_val);251 try self.emitWValue(arg_val);
220 }252 }
221253
222 try self.code.append(0x10); // call254 try self.code.append(wasm.opcode(.call));
223255
224 // The function index immediate argument will be filled in using this data256 // The function index immediate argument will be filled in using this data
225 // in link.Wasm.flush().257 // in link.Wasm.flush().
...@@ -228,10 +260,14 @@ pub const Context = struct {...@@ -228,10 +260,14 @@ pub const Context = struct {
228 .decl = target,260 .decl = target,
229 });261 });
230262
231 return WValue.none;263 return .none;
232 }264 }
233265
234 fn genAlloc(self: *Context, inst: *Inst.NoOp) InnerError!WValue {266 fn genAlloc(self: *Context, inst: *Inst.NoOp) InnerError!WValue {
267 const elem_type = inst.base.ty.elemType();
268 const valtype = try self.genValtype(inst.base.src, elem_type);
269 try self.locals.append(self.gpa, valtype);
270
235 defer self.local_index += 1;271 defer self.local_index += 1;
236 return WValue{ .local = self.local_index };272 return WValue{ .local = self.local_index };
237 }273 }
...@@ -243,15 +279,14 @@ pub const Context = struct {...@@ -243,15 +279,14 @@ pub const Context = struct {
243 const rhs = self.resolveInst(inst.rhs);279 const rhs = self.resolveInst(inst.rhs);
244 try self.emitWValue(rhs);280 try self.emitWValue(rhs);
245281
246 try writer.writeByte(0x21); // local.set282 try writer.writeByte(wasm.opcode(.local_set));
247 try leb.writeULEB128(writer, lhs.local);283 try leb.writeULEB128(writer, lhs.local);
248 return WValue.none;284 return .none;
249 }285 }
250286
251 fn genLoad(self: *Context, inst: *Inst.UnOp) InnerError!WValue {287 fn genLoad(self: *Context, inst: *Inst.UnOp) InnerError!WValue {
252 const operand = self.resolveInst(inst.operand);288 const operand = self.resolveInst(inst.operand);
253 try self.emitWValue(operand);289 return operand;
254 return WValue.none;
255 }290 }
256291
257 fn genArg(self: *Context, inst: *Inst.Arg) InnerError!WValue {292 fn genArg(self: *Context, inst: *Inst.Arg) InnerError!WValue {
...@@ -267,44 +302,44 @@ pub const Context = struct {...@@ -267,44 +302,44 @@ pub const Context = struct {
267 try self.emitWValue(lhs);302 try self.emitWValue(lhs);
268 try self.emitWValue(rhs);303 try self.emitWValue(rhs);
269304
270 const opcode: u8 = switch (inst.base.ty.tag()) {305 const opcode: wasm.Opcode = switch (inst.base.ty.tag()) {
271 .u32, .i32 => 0x6A, //i32.add306 .u32, .i32 => .i32_add,
272 .u64, .i64 => 0x7C, //i64.add307 .u64, .i64 => .i64_add,
273 .f32 => 0x92, //f32.add308 .f32 => .f32_add,
274 .f64 => 0xA0, //f64.add309 .f64 => .f64_add,
275 else => return self.fail(inst.base.src, "TODO - Implement wasm genAdd for type '{s}'", .{inst.base.ty.tag()}),310 else => return self.fail(inst.base.src, "TODO - Implement wasm genAdd for type '{s}'", .{inst.base.ty.tag()}),
276 };311 };
277312
278 try self.code.append(opcode);313 try self.code.append(wasm.opcode(opcode));
279 return WValue.none;314 return .none;
280 }315 }
281316
282 fn emitConstant(self: *Context, inst: *Inst.Constant) InnerError!void {317 fn emitConstant(self: *Context, inst: *Inst.Constant) InnerError!void {
283 const writer = self.code.writer();318 const writer = self.code.writer();
284 switch (inst.base.ty.tag()) {319 switch (inst.base.ty.tag()) {
285 .u32 => {320 .u32 => {
286 try writer.writeByte(0x41); // i32.const321 try writer.writeByte(wasm.opcode(.i32_const));
287 try leb.writeILEB128(writer, inst.val.toUnsignedInt());322 try leb.writeILEB128(writer, inst.val.toUnsignedInt());
288 },323 },
289 .i32 => {324 .i32 => {
290 try writer.writeByte(0x41); // i32.const325 try writer.writeByte(wasm.opcode(.i32_const));
291 try leb.writeILEB128(writer, inst.val.toSignedInt());326 try leb.writeILEB128(writer, inst.val.toSignedInt());
292 },327 },
293 .u64 => {328 .u64 => {
294 try writer.writeByte(0x42); // i64.const329 try writer.writeByte(wasm.opcode(.i64_const));
295 try leb.writeILEB128(writer, inst.val.toUnsignedInt());330 try leb.writeILEB128(writer, inst.val.toUnsignedInt());
296 },331 },
297 .i64 => {332 .i64 => {
298 try writer.writeByte(0x42); // i64.const333 try writer.writeByte(wasm.opcode(.i64_const));
299 try leb.writeILEB128(writer, inst.val.toSignedInt());334 try leb.writeILEB128(writer, inst.val.toSignedInt());
300 },335 },
301 .f32 => {336 .f32 => {
302 try writer.writeByte(0x43); // f32.const337 try writer.writeByte(wasm.opcode(.f32_const));
303 // TODO: enforce LE byte order338 // TODO: enforce LE byte order
304 try writer.writeAll(mem.asBytes(&inst.val.toFloat(f32)));339 try writer.writeAll(mem.asBytes(&inst.val.toFloat(f32)));
305 },340 },
306 .f64 => {341 .f64 => {
307 try writer.writeByte(0x44); // f64.const342 try writer.writeByte(wasm.opcode(.f64_const));
308 // TODO: enforce LE byte order343 // TODO: enforce LE byte order
309 try writer.writeAll(mem.asBytes(&inst.val.toFloat(f64)));344 try writer.writeAll(mem.asBytes(&inst.val.toFloat(f64)));
310 },345 },
...@@ -312,4 +347,162 @@ pub const Context = struct {...@@ -312,4 +347,162 @@ pub const Context = struct {
312 else => |ty| return self.fail(inst.base.src, "Wasm TODO: emitConstant for type {s}", .{ty}),347 else => |ty| return self.fail(inst.base.src, "Wasm TODO: emitConstant for type {s}", .{ty}),
313 }348 }
314 }349 }
350
351 fn genBlock(self: *Context, block: *Inst.Block) InnerError!WValue {
352 const block_ty = try self.genBlockType(block.base.src, block.base.ty);
353
354 block.codegen = .{
355 // we don't use relocs, so using `relocs` is illegal behaviour.
356 .relocs = undefined,
357 // Here we set the current block idx, so conditions know the depth to jump
358 // to when breaking out. This will be set to .none when it is found again within
359 // the same block
360 .mcv = @bitCast(AnyMCValue, WValue{ .block_idx = self.block_depth }),
361 };
362 self.block_depth += 1;
363
364 try self.code.append(wasm.opcode(.block));
365 try self.code.append(block_ty);
366 try self.genBody(block.body);
367 try self.code.append(wasm.opcode(.end));
368
369 self.block_depth -= 1;
370 return .none;
371 }
372
373 fn genLoop(self: *Context, loop: *Inst.Loop) InnerError!WValue {
374 const loop_ty = try self.genBlockType(loop.base.src, loop.base.ty);
375
376 try self.code.append(wasm.opcode(.loop));
377 try self.code.append(loop_ty);
378 self.block_depth += 1;
379 try self.genBody(loop.body);
380 self.block_depth -= 1;
381
382 try self.code.append(wasm.opcode(.end));
383
384 return .none;
385 }
386
387 fn genCondBr(self: *Context, condbr: *Inst.CondBr) InnerError!WValue {
388 const condition = self.resolveInst(condbr.condition);
389 const writer = self.code.writer();
390
391 // insert blocks at the position of `offset` so
392 // the condition can jump to it
393 const offset = condition.code_offset;
394 try self.code.insert(offset, wasm.opcode(.block));
395 try self.code.insert(offset, try self.genBlockType(condbr.base.src, condbr.base.ty));
396
397 // we inserted the block in front of the condition
398 // so now check if condition matches. If not, break outside this block
399 // and continue with the regular codepath
400 try writer.writeByte(wasm.opcode(.br_if));
401 try leb.writeULEB128(writer, @as(u32, 0));
402
403 // else body in case condition does not match
404 try self.genBody(condbr.else_body);
405
406 // finally, tell wasm we have reached the end of the block we inserted above
407 try writer.writeByte(wasm.opcode(.end));
408
409 // Outer block that matches the condition
410 try self.genBody(condbr.then_body);
411
412 return .none;
413 }
414
415 fn genCmp(self: *Context, inst: *Inst.BinOp, op: std.math.CompareOperator) InnerError!WValue {
416 const ty = inst.lhs.ty.tag();
417
418 // save offset, so potential conditions can insert blocks in front of
419 // the comparison that we can later jump back to
420 const offset = self.code.items.len - 1;
421
422 const lhs = self.resolveInst(inst.lhs);
423 const rhs = self.resolveInst(inst.rhs);
424
425 try self.emitWValue(lhs);
426 try self.emitWValue(rhs);
427
428 const opcode_maybe: ?wasm.Opcode = switch (op) {
429 .lt => @as(?wasm.Opcode, switch (ty) {
430 .i32 => .i32_lt_s,
431 .u32 => .i32_lt_u,
432 .i64 => .i64_lt_s,
433 .u64 => .i64_lt_u,
434 .f32 => .f32_lt,
435 .f64 => .f64_lt,
436 else => null,
437 }),
438 .lte => @as(?wasm.Opcode, switch (ty) {
439 .i32 => .i32_le_s,
440 .u32 => .i32_le_u,
441 .i64 => .i64_le_s,
442 .u64 => .i64_le_u,
443 .f32 => .f32_le,
444 .f64 => .f64_le,
445 else => null,
446 }),
447 .eq => @as(?wasm.Opcode, switch (ty) {
448 .i32, .u32 => .i32_eq,
449 .i64, .u64 => .i64_eq,
450 .f32 => .f32_eq,
451 .f64 => .f64_eq,
452 else => null,
453 }),
454 .gte => @as(?wasm.Opcode, switch (ty) {
455 .i32 => .i32_ge_s,
456 .u32 => .i32_ge_u,
457 .i64 => .i64_ge_s,
458 .u64 => .i64_ge_u,
459 .f32 => .f32_ge,
460 .f64 => .f64_ge,
461 else => null,
462 }),
463 .gt => @as(?wasm.Opcode, switch (ty) {
464 .i32 => .i32_gt_s,
465 .u32 => .i32_gt_u,
466 .i64 => .i64_gt_s,
467 .u64 => .i64_gt_u,
468 .f32 => .f32_gt,
469 .f64 => .f64_gt,
470 else => null,
471 }),
472 .neq => @as(?wasm.Opcode, switch (ty) {
473 .i32, .u32 => .i32_ne,
474 .i64, .u64 => .i64_ne,
475 .f32 => .f32_ne,
476 .f64 => .f64_ne,
477 else => null,
478 }),
479 };
480
481 const opcode = opcode_maybe orelse
482 return self.fail(inst.base.src, "TODO - Wasm genCmp for type '{s}' and operator '{s}'", .{ ty, @tagName(op) });
483
484 try self.code.append(wasm.opcode(opcode));
485 return WValue{ .code_offset = offset };
486 }
487
488 fn genBr(self: *Context, br: *Inst.Br) InnerError!WValue {
489 // of operand has codegen bits we should break with a value
490 if (br.operand.ty.hasCodeGenBits()) {
491 const operand = self.resolveInst(br.operand);
492 try self.emitWValue(operand);
493 }
494
495 // if the block contains a block_idx, do a relative jump to it
496 // if `wvalue` was already 'consumed', simply break out of current block
497 const wvalue = @bitCast(WValue, br.block.codegen.mcv);
498 const idx: u32 = if (wvalue == .block_idx) blk: {
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();
504 try writer.writeByte(wasm.opcode(.br));
505 try leb.writeULEB128(writer, idx);
506 return WValue.none;
507 }
315};508};
src/link/Wasm.zig+3-2
...@@ -103,13 +103,14 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {...@@ -103,13 +103,14 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
103103
104 var context = codegen.Context{104 var context = codegen.Context{
105 .gpa = self.base.allocator,105 .gpa = self.base.allocator,
106 .values = codegen.ValueTable.init(self.base.allocator),106 .values = .{},
107 .code = managed_code,107 .code = managed_code,
108 .func_type_data = managed_functype,108 .func_type_data = managed_functype,
109 .decl = decl,109 .decl = decl,
110 .err_msg = undefined,110 .err_msg = undefined,
111 .locals = .{},
111 };112 };
112 defer context.values.deinit();113 defer context.deinit();
113114
114 // generate the 'code' section for the function declaration115 // generate the 'code' section for the function declaration
115 context.gen() catch |err| switch (err) {116 context.gen() catch |err| switch (err) {