authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-07-23 16:41:04+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-07-24 19:49:25+02:00
logcb41f0e58d4788806aac0bfece2e298c3c79b737
tree7aae4e19719a02c211f2134fabd4e84d660a49e0
parentad38fc11470ad3c2e063ad5739d4f4e84dd495f1
signature Commit is signed but in an unrecognized format.

switchbr: When prongs are sparse values, use if/else-chain


1 files changed, 69 insertions(+), 28 deletions(-)

src/codegen/wasm.zig+69-28
...@@ -1275,12 +1275,17 @@ pub const Context = struct {...@@ -1275,12 +1275,17 @@ pub const Context = struct {
1275 const blocktype = wasm.block_empty;1275 const blocktype = wasm.block_empty;
1276 const pl_op = self.air.instructions.items(.data)[inst].pl_op;1276 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
1277 const target = self.resolveInst(pl_op.operand);1277 const target = self.resolveInst(pl_op.operand);
1278 const target_ty = self.air.typeOf(pl_op.operand);
1278 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);1279 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);
1279 var extra_index: usize = switch_br.end;1280 var extra_index: usize = switch_br.end;
1280 var case_i: u32 = 0;1281 var case_i: u32 = 0;
12811282
1282 // a map that maps each value with its index and body1283 // 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 var map = std.AutoArrayHashMap(u32, struct {
1285 index: u32,
1286 body: []const Air.Inst.Index,
1287 value: Value,
1288 }).init(self.gpa);
1284 defer map.deinit();1289 defer map.deinit();
12851290
1286 var lowest: u32 = 0;1291 var lowest: u32 = 0;
...@@ -1292,51 +1297,87 @@ pub const Context = struct {...@@ -1292,51 +1297,87 @@ pub const Context = struct {
1292 extra_index = case.end + items.len + case_body.len;1297 extra_index = case.end + items.len + case_body.len;
12931298
1294 for (items) |ref| {1299 for (items) |ref| {
1295 const item_val = @intCast(u32, self.air.value(ref).?.toUnsignedInt());1300 const item_val = self.air.value(ref).?;
1296 if (item_val < lowest) {1301 // safe to truncate the values as we only use them when
1297 lowest = item_val;1302 // the target's bits is 32 or lower.
1303 const int_val = @truncate(u32, item_val.toUnsignedInt());
1304 if (int_val < lowest) {
1305 lowest = int_val;
1298 }1306 }
1299 if (item_val > highest) {1307 if (int_val > highest) {
1300 highest = item_val;1308 highest = int_val;
1301 }1309 }
1302 try map.put(item_val, .{ .index = case_i, .body = case_body });1310 try map.put(int_val, .{ .index = case_i, .body = case_body, .value = item_val });
1303 }1311 }
13041312
1305 try self.startBlock(.block, blocktype, null);1313 try self.startBlock(.block, blocktype, null);
1306 }1314 }
13071315
1316 // When the highest and lowest values are seperated by '50',
1317 // we define it as sparse and use an if/else-chain, rather than a jump table.
1318 // When the target is an integer size larger than u32, we have no way to use the value
1319 // as an index, therefore we also use an if/else-chain for those cases.
1320 // TODO: Benchmark this to find a proper value, LLVM seems to draw the line at '40~45'.
1321 const is_sparse = target_ty.intInfo(self.target).bits > 32 or highest - lowest > 50;
1322
1308 const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len];1323 const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len];
1309 if (else_body.len != 0) {1324 const has_else_body = else_body.len != 0;
1325 if (has_else_body) {
1310 try self.startBlock(.block, blocktype, null);1326 try self.startBlock(.block, blocktype, null);
1311 }1327 }
13121328
1313 // Generate the jump table 'br_table'.1329 if (!is_sparse) {
1314 // The value 'target' represents the index into the table.1330 // Generate the jump table 'br_table' when the prongs are not sparse.
1315 // Each index in the table represents a label to the branch1331 // The value 'target' represents the index into the table.
1316 // to jump to.1332 // Each index in the table represents a label to the branch
1317 try self.startBlock(.block, blocktype, null);1333 // to jump to.
1318 try self.emitWValue(target);1334 try self.startBlock(.block, blocktype, null);
1319 try self.code.append(wasm.opcode(.br_table));1335 try self.emitWValue(target);
1320 try leb.writeULEB128(self.code.writer(), highest - lowest + 1);1336 try self.code.append(wasm.opcode(.br_table));
1321 while (lowest <= highest) : (lowest += 1) {1337 const depth = highest - lowest + @boolToInt(has_else_body);
1322 const idx = if (map.get(lowest)) |value| blk: {1338 try leb.writeULEB128(self.code.writer(), depth);
1323 break :blk value.index + 1;1339 while (lowest <= highest) : (lowest += 1) {
1324 } else 0;1340 const idx = if (map.get(lowest)) |value| blk: {
1325 try leb.writeULEB128(self.code.writer(), idx);1341 break :blk value.index;
1326 } else if (else_body.len != 0) {1342 } else if (has_else_body) case_i else unreachable;
1327 try leb.writeULEB128(self.code.writer(), @as(u32, 0)); // default branch1343 try leb.writeULEB128(self.code.writer(), idx);
1328 }1344 } else if (has_else_body) {
1329 try self.endBlock();1345 try leb.writeULEB128(self.code.writer(), @as(u32, case_i)); // default branch
13301346 }
1331 if (else_body.len != 0) {
1332 try self.genBody(else_body);
1333 try self.endBlock();1347 try self.endBlock();
1334 }1348 }
13351349
1350 const signedness: std.builtin.Signedness = blk: {
1351 // by default we tell the operand type is unsigned (i.e. bools and enum values)
1352 if (target_ty.zigTypeTag() != .Int) break :blk .unsigned;
1353
1354 // incase of an actual integer, we emit the correct signedness
1355 break :blk target_ty.intInfo(self.target).signedness;
1356 };
1357
1336 for (map.values()) |val| {1358 for (map.values()) |val| {
1359 // when sparse, we use if/else-chain, so emit conditional checks
1360 if (is_sparse) {
1361 try self.emitWValue(target);
1362 try self.emitConstant(val.value, target_ty);
1363 const opcode = buildOpcode(.{
1364 .valtype1 = try self.typeToValtype(target_ty),
1365 .op = .ne, // not equal, because we want to jump out of this block if it does not match the condition.
1366 .signedness = signedness,
1367 });
1368 try self.code.append(wasm.opcode(opcode));
1369 try self.code.append(wasm.opcode(.br_if));
1370 try leb.writeULEB128(self.code.writer(), @as(u32, 0));
1371 }
1337 try self.genBody(val.body);1372 try self.genBody(val.body);
1338 try self.endBlock();1373 try self.endBlock();
1339 }1374 }
1375
1376 if (has_else_body) {
1377 try self.genBody(else_body);
1378 try self.endBlock();
1379 }
1380
1340 return .none;1381 return .none;
1341 }1382 }
13421383