authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-07-22 17:51:23+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-07-24 19:49:24+02:00
logad38fc11470ad3c2e063ad5739d4f4e84dd495f1
tree4c8f76bee15790dc4d36453e29551cc5b2586041
parent7b8cb881df7e034a8626caabf355055ee81a0fef
signature Commit is signed but in an unrecognized format.

wasm: Rewrite switch_br to use `br_table` instead

This is an initial version, todo: - Also make this work for u64 values, as the table must be indexed by u32. - Add support for signed integers. - Add support for enums.

1 files changed, 64 insertions(+), 51 deletions(-)

src/codegen/wasm.zig+64-51
...@@ -1271,60 +1271,73 @@ pub const Context = struct {...@@ -1271,60 +1271,73 @@ pub const Context = struct {
1271 }1271 }
12721272
1273 fn airSwitchBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {1273 fn airSwitchBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1274 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
1275 const extra = self.air.extraData(Air.SwitchBr, pl_op.payload);
1276 const cases = self.air.extra[extra.end..][0..extra.data.cases_len];
1277 const else_body = self.air.extra[extra.end + cases.len ..][0..extra.data.else_body_len];
1278
1279 const target = self.resolveInst(pl_op.operand);
1280 const target_ty = self.air.typeOf(pl_op.operand);
1281 const valtype = try self.typeToValtype(target_ty);
1282 // result type is always 'noreturn'1274 // result type is always 'noreturn'
1283 const blocktype = wasm.block_empty;1275 const blocktype = wasm.block_empty;
1276 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
1277 const target = self.resolveInst(pl_op.operand);
1278 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);
1279 var extra_index: usize = switch_br.end;
1280 var case_i: u32 = 0;
1281
1282 // a map that maps each value with its index and body
1283 var map = std.AutoArrayHashMap(u32, struct { index: u32, body: []const Air.Inst.Index }).init(self.gpa);
1284 defer map.deinit();
1285
1286 var lowest: u32 = 0;
1287 var highest: u32 = 0;
1288 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
1289 const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
1290 const items = @bitCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]);
1291 const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len];
1292 extra_index = case.end + items.len + case_body.len;
1293
1294 for (items) |ref| {
1295 const item_val = @intCast(u32, self.air.value(ref).?.toUnsignedInt());
1296 if (item_val < lowest) {
1297 lowest = item_val;
1298 }
1299 if (item_val > highest) {
1300 highest = item_val;
1301 }
1302 try map.put(item_val, .{ .index = case_i, .body = case_body });
1303 }
1304
1305 try self.startBlock(.block, blocktype, null);
1306 }
12841307
1285 _ = valtype;1308 const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len];
1286 _ = blocktype;1309 if (else_body.len != 0) {
1287 _ = target;1310 try self.startBlock(.block, blocktype, null);
1288 _ = else_body;1311 }
1289 return self.fail("TODO implement wasm codegen for switch", .{});1312
1290 //const signedness: std.builtin.Signedness = blk: {1313 // Generate the jump table 'br_table'.
1291 // // by default we tell the operand type is unsigned (i.e. bools and enum values)1314 // The value 'target' represents the index into the table.
1292 // if (target_ty.zigTypeTag() != .Int) break :blk .unsigned;1315 // Each index in the table represents a label to the branch
12931316 // to jump to.
1294 // // incase of an actual integer, we emit the correct signedness1317 try self.startBlock(.block, blocktype, null);
1295 // break :blk target_ty.intInfo(self.target).signedness;1318 try self.emitWValue(target);
1296 //};1319 try self.code.append(wasm.opcode(.br_table));
1297 //for (cases) |case_idx| {1320 try leb.writeULEB128(self.code.writer(), highest - lowest + 1);
1298 // const case = self.air.extraData(Air.SwitchBr.Case, case_idx);1321 while (lowest <= highest) : (lowest += 1) {
1299 // const case_body = self.air.extra[case.end..][0..case.data.body_len];1322 const idx = if (map.get(lowest)) |value| blk: {
13001323 break :blk value.index + 1;
1301 // // create a block for each case, when the condition does not match we break out of it1324 } else 0;
1302 // try self.startBlock(.block, blocktype, null);1325 try leb.writeULEB128(self.code.writer(), idx);
1303 // try self.emitWValue(target);1326 } else if (else_body.len != 0) {
13041327 try leb.writeULEB128(self.code.writer(), @as(u32, 0)); // default branch
1305 // const val = self.air.value(case.data.item).?;1328 }
1306 // try self.emitConstant(val, target_ty);1329 try self.endBlock();
1307 // const opcode = buildOpcode(.{1330
1308 // .valtype1 = valtype,1331 if (else_body.len != 0) {
1309 // .op = .ne, // not equal because we jump out the block if it does not match the condition1332 try self.genBody(else_body);
1310 // .signedness = signedness,1333 try self.endBlock();
1311 // });1334 }
1312 // try self.code.append(wasm.opcode(opcode));1335
1313 // try self.code.append(wasm.opcode(.br_if));1336 for (map.values()) |val| {
1314 // try leb.writeULEB128(self.code.writer(), @as(u32, 0));1337 try self.genBody(val.body);
13151338 try self.endBlock();
1316 // // emit our block code1339 }
1317 // try self.genBody(case_body);1340 return .none;
1318
1319 // // end the block we created earlier
1320 // try self.endBlock();
1321 //}
1322
1323 //// finally, emit the else case if it exists. Here we will not have to
1324 //// check for a condition, so also no need to emit a block.
1325 //try self.genBody(else_body);
1326
1327 //return .none;
1328 }1341 }
13291342
1330 fn airIsErr(self: *Context, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue {1343 fn airIsErr(self: *Context, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue {