authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-07-24 17:50:26+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-07-24 19:49:25+02:00
log5d98abd5703607884844e8b8165dcc2238389c57
tree49ace4ff6fe1196dbff7e174b62b151f1ceff4bb
parent72149ae7e4a826d1657d82c367596e06ff771734
signaturelock-open Commit is signed but in an unrecognized format.

Support multi-value prongs


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

src/codegen/wasm.zig+64-27
......@@ -979,10 +979,15 @@ pub const Context = struct {
979979 .valtype1 = try self.typeToValtype(ty),
980980 });
981981 try writer.writeByte(wasm.opcode(opcode));
982 const int_info = ty.intInfo(self.target);
982983 // write constant
983 switch (ty.intInfo(self.target).signedness) {
984 switch (int_info.signedness) {
984985 .signed => try leb.writeILEB128(writer, value.toSignedInt()),
985 .unsigned => try leb.writeILEB128(writer, value.toUnsignedInt()),
986 .unsigned => switch (int_info.bits) {
987 0...32 => try leb.writeILEB128(writer, @bitCast(i32, @intCast(u32, value.toUnsignedInt()))),
988 33...64 => try leb.writeILEB128(writer, @bitCast(i64, value.toUnsignedInt())),
989 else => |bits| return self.fail("Wasm TODO: emitConstant for integer with {d} bits", .{bits}),
990 },
986991 }
987992 },
988993 .Bool => {
......@@ -1280,13 +1285,15 @@ pub const Context = struct {
12801285 var extra_index: usize = switch_br.end;
12811286 var case_i: u32 = 0;
12821287
1283 // a map that maps each value with its index and body
1284 var map = std.AutoArrayHashMap(i32, struct {
1285 index: u32,
1288 // a list that maps each value with its value and body based on the order inside the list.
1289 const CaseValue = struct { integer: i32, value: Value };
1290 var case_list = try std.ArrayList(struct {
1291 values: []const CaseValue,
12861292 body: []const Air.Inst.Index,
1287 value: Value,
1288 }).init(self.gpa);
1289 defer map.deinit();
1293 }).initCapacity(self.gpa, switch_br.data.cases_len);
1294 defer for (case_list.items) |case| {
1295 self.gpa.free(case.values);
1296 } else case_list.deinit();
12901297
12911298 var lowest: i32 = 0;
12921299 var highest: i32 = 0;
......@@ -1295,8 +1302,10 @@ pub const Context = struct {
12951302 const items = @bitCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]);
12961303 const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len];
12971304 extra_index = case.end + items.len + case_body.len;
1305 const values = try self.gpa.alloc(CaseValue, items.len);
1306 errdefer self.gpa.free(values);
12981307
1299 for (items) |ref| {
1308 for (items) |ref, i| {
13001309 const item_val = self.air.value(ref).?;
13011310 const int_val: i32 = blk: {
13021311 if (target_ty.intInfo(self.target).signedness == .signed) {
......@@ -1305,7 +1314,7 @@ pub const Context = struct {
13051314 break :blk @truncate(i32, item_val.toSignedInt());
13061315 }
13071316
1308 break :blk @intCast(i32, @truncate(u31, item_val.toUnsignedInt()) - 1);
1317 break :blk @bitCast(i32, @truncate(u32, item_val.toUnsignedInt()));
13091318 };
13101319 if (int_val < lowest) {
13111320 lowest = int_val;
......@@ -1313,9 +1322,10 @@ pub const Context = struct {
13131322 if (int_val > highest) {
13141323 highest = int_val;
13151324 }
1316 try map.put(int_val, .{ .index = case_i, .body = case_body, .value = item_val });
1325 values[i] = .{ .integer = int_val, .value = item_val };
13171326 }
13181327
1328 case_list.appendAssumeCapacity(.{ .values = values, .body = case_body });
13191329 try self.startBlock(.block, blocktype, null);
13201330 }
13211331
......@@ -1350,9 +1360,15 @@ pub const Context = struct {
13501360 const depth = highest - lowest + @boolToInt(has_else_body);
13511361 try leb.writeILEB128(self.code.writer(), depth);
13521362 while (lowest <= highest) : (lowest += 1) {
1353 const idx = if (map.get(lowest)) |value| blk: {
1354 break :blk value.index;
1355 } else if (has_else_body) case_i else unreachable;
1363 // idx represents the branch we jump to
1364 const idx = blk: {
1365 for (case_list.items) |case, idx| {
1366 for (case.values) |case_value| {
1367 if (case_value.integer == lowest) break :blk @intCast(u32, idx);
1368 }
1369 }
1370 break :blk if (has_else_body) case_i else unreachable;
1371 };
13561372 try leb.writeULEB128(self.code.writer(), idx);
13571373 } else if (has_else_body) {
13581374 try leb.writeULEB128(self.code.writer(), @as(u32, case_i)); // default branch
......@@ -1368,21 +1384,43 @@ pub const Context = struct {
13681384 break :blk target_ty.intInfo(self.target).signedness;
13691385 };
13701386
1371 for (map.values()) |val| {
1387 for (case_list.items) |case| {
13721388 // when sparse, we use if/else-chain, so emit conditional checks
13731389 if (is_sparse) {
1374 try self.emitWValue(target);
1375 try self.emitConstant(val.value, target_ty);
1376 const opcode = buildOpcode(.{
1377 .valtype1 = try self.typeToValtype(target_ty),
1378 .op = .ne, // not equal, because we want to jump out of this block if it does not match the condition.
1379 .signedness = signedness,
1380 });
1381 try self.code.append(wasm.opcode(opcode));
1382 try self.code.append(wasm.opcode(.br_if));
1383 try leb.writeULEB128(self.code.writer(), @as(u32, 0));
1390 // for single value prong we can emit a simple if
1391 if (case.values.len == 1) {
1392 try self.emitWValue(target);
1393 try self.emitConstant(case.values[0].value, target_ty);
1394 const opcode = buildOpcode(.{
1395 .valtype1 = try self.typeToValtype(target_ty),
1396 .op = .ne, // not equal, because we want to jump out of this block if it does not match the condition.
1397 .signedness = signedness,
1398 });
1399 try self.code.append(wasm.opcode(opcode));
1400 try self.code.append(wasm.opcode(.br_if));
1401 try leb.writeULEB128(self.code.writer(), @as(u32, 0));
1402 } else {
1403 // in multi-value prongs we must check if any prongs match the target value.
1404 try self.startBlock(.block, blocktype, null);
1405 for (case.values) |value| {
1406 try self.emitWValue(target);
1407 try self.emitConstant(value.value, target_ty);
1408 const opcode = buildOpcode(.{
1409 .valtype1 = try self.typeToValtype(target_ty),
1410 .op = .eq,
1411 .signedness = signedness,
1412 });
1413 try self.code.append(wasm.opcode(opcode));
1414 try self.code.append(wasm.opcode(.br_if));
1415 try leb.writeULEB128(self.code.writer(), @as(u32, 0));
1416 }
1417 // value did not match any of the prong values
1418 try self.code.append(wasm.opcode(.br));
1419 try leb.writeULEB128(self.code.writer(), @as(u32, 1));
1420 try self.endBlock();
1421 }
13841422 }
1385 try self.genBody(val.body);
1423 try self.genBody(case.body);
13861424 try self.endBlock();
13871425 }
13881426
......@@ -1390,7 +1428,6 @@ pub const Context = struct {
13901428 try self.genBody(else_body);
13911429 try self.endBlock();
13921430 }
1393
13941431 return .none;
13951432 }
13961433