| ... | @@ -979,10 +979,15 @@ pub const Context = struct { | ... | @@ -979,10 +979,15 @@ pub const Context = struct { |
| 979 | .valtype1 = try self.typeToValtype(ty), | 979 | .valtype1 = try self.typeToValtype(ty), |
| 980 | }); | 980 | }); |
| 981 | try writer.writeByte(wasm.opcode(opcode)); | 981 | try writer.writeByte(wasm.opcode(opcode)); |
| | 982 | const int_info = ty.intInfo(self.target); |
| 982 | // write constant | 983 | // write constant |
| 983 | switch (ty.intInfo(self.target).signedness) { | 984 | switch (int_info.signedness) { |
| 984 | .signed => try leb.writeILEB128(writer, value.toSignedInt()), | 985 | .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 | }, |
| 986 | } | 991 | } |
| 987 | }, | 992 | }, |
| 988 | .Bool => { | 993 | .Bool => { |
| ... | @@ -1280,13 +1285,15 @@ pub const Context = struct { | ... | @@ -1280,13 +1285,15 @@ pub const Context = struct { |
| 1280 | var extra_index: usize = switch_br.end; | 1285 | var extra_index: usize = switch_br.end; |
| 1281 | var case_i: u32 = 0; | 1286 | var case_i: u32 = 0; |
| 1282 | | 1287 | |
| 1283 | // a map that maps each value with its index and body | 1288 | // a list that maps each value with its value and body based on the order inside the list. |
| 1284 | var map = std.AutoArrayHashMap(i32, struct { | 1289 | const CaseValue = struct { integer: i32, value: Value }; |
| 1285 | index: u32, | 1290 | var case_list = try std.ArrayList(struct { |
| | 1291 | values: []const CaseValue, |
| 1286 | body: []const Air.Inst.Index, | 1292 | body: []const Air.Inst.Index, |
| 1287 | value: Value, | 1293 | }).initCapacity(self.gpa, switch_br.data.cases_len); |
| 1288 | }).init(self.gpa); | 1294 | defer for (case_list.items) |case| { |
| 1289 | defer map.deinit(); | 1295 | self.gpa.free(case.values); |
| | 1296 | } else case_list.deinit(); |
| 1290 | | 1297 | |
| 1291 | var lowest: i32 = 0; | 1298 | var lowest: i32 = 0; |
| 1292 | var highest: i32 = 0; | 1299 | var highest: i32 = 0; |
| ... | @@ -1295,8 +1302,10 @@ pub const Context = struct { | ... | @@ -1295,8 +1302,10 @@ pub const Context = struct { |
| 1295 | const items = @bitCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]); | 1302 | const items = @bitCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]); |
| 1296 | const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len]; | 1303 | const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len]; |
| 1297 | extra_index = case.end + items.len + case_body.len; | 1304 | 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); |
| 1298 | | 1307 | |
| 1299 | for (items) |ref| { | 1308 | for (items) |ref, i| { |
| 1300 | const item_val = self.air.value(ref).?; | 1309 | const item_val = self.air.value(ref).?; |
| 1301 | const int_val: i32 = blk: { | 1310 | const int_val: i32 = blk: { |
| 1302 | if (target_ty.intInfo(self.target).signedness == .signed) { | 1311 | if (target_ty.intInfo(self.target).signedness == .signed) { |
| ... | @@ -1305,7 +1314,7 @@ pub const Context = struct { | ... | @@ -1305,7 +1314,7 @@ pub const Context = struct { |
| 1305 | break :blk @truncate(i32, item_val.toSignedInt()); | 1314 | break :blk @truncate(i32, item_val.toSignedInt()); |
| 1306 | } | 1315 | } |
| 1307 | | 1316 | |
| 1308 | break :blk @intCast(i32, @truncate(u31, item_val.toUnsignedInt()) - 1); | 1317 | break :blk @bitCast(i32, @truncate(u32, item_val.toUnsignedInt())); |
| 1309 | }; | 1318 | }; |
| 1310 | if (int_val < lowest) { | 1319 | if (int_val < lowest) { |
| 1311 | lowest = int_val; | 1320 | lowest = int_val; |
| ... | @@ -1313,9 +1322,10 @@ pub const Context = struct { | ... | @@ -1313,9 +1322,10 @@ pub const Context = struct { |
| 1313 | if (int_val > highest) { | 1322 | if (int_val > highest) { |
| 1314 | highest = int_val; | 1323 | highest = int_val; |
| 1315 | } | 1324 | } |
| 1316 | try map.put(int_val, .{ .index = case_i, .body = case_body, .value = item_val }); | 1325 | values[i] = .{ .integer = int_val, .value = item_val }; |
| 1317 | } | 1326 | } |
| 1318 | | 1327 | |
| | 1328 | case_list.appendAssumeCapacity(.{ .values = values, .body = case_body }); |
| 1319 | try self.startBlock(.block, blocktype, null); | 1329 | try self.startBlock(.block, blocktype, null); |
| 1320 | } | 1330 | } |
| 1321 | | 1331 | |
| ... | @@ -1350,9 +1360,15 @@ pub const Context = struct { | ... | @@ -1350,9 +1360,15 @@ pub const Context = struct { |
| 1350 | const depth = highest - lowest + @boolToInt(has_else_body); | 1360 | const depth = highest - lowest + @boolToInt(has_else_body); |
| 1351 | try leb.writeILEB128(self.code.writer(), depth); | 1361 | try leb.writeILEB128(self.code.writer(), depth); |
| 1352 | while (lowest <= highest) : (lowest += 1) { | 1362 | while (lowest <= highest) : (lowest += 1) { |
| 1353 | const idx = if (map.get(lowest)) |value| blk: { | 1363 | // idx represents the branch we jump to |
| 1354 | break :blk value.index; | 1364 | const idx = blk: { |
| 1355 | } else if (has_else_body) case_i else unreachable; | 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 | }; |
| 1356 | try leb.writeULEB128(self.code.writer(), idx); | 1372 | try leb.writeULEB128(self.code.writer(), idx); |
| 1357 | } else if (has_else_body) { | 1373 | } else if (has_else_body) { |
| 1358 | try leb.writeULEB128(self.code.writer(), @as(u32, case_i)); // default branch | 1374 | try leb.writeULEB128(self.code.writer(), @as(u32, case_i)); // default branch |
| ... | @@ -1368,21 +1384,43 @@ pub const Context = struct { | ... | @@ -1368,21 +1384,43 @@ pub const Context = struct { |
| 1368 | break :blk target_ty.intInfo(self.target).signedness; | 1384 | break :blk target_ty.intInfo(self.target).signedness; |
| 1369 | }; | 1385 | }; |
| 1370 | | 1386 | |
| 1371 | for (map.values()) |val| { | 1387 | for (case_list.items) |case| { |
| 1372 | // when sparse, we use if/else-chain, so emit conditional checks | 1388 | // when sparse, we use if/else-chain, so emit conditional checks |
| 1373 | if (is_sparse) { | 1389 | if (is_sparse) { |
| 1374 | try self.emitWValue(target); | 1390 | // for single value prong we can emit a simple if |
| 1375 | try self.emitConstant(val.value, target_ty); | 1391 | if (case.values.len == 1) { |
| 1376 | const opcode = buildOpcode(.{ | 1392 | try self.emitWValue(target); |
| 1377 | .valtype1 = try self.typeToValtype(target_ty), | 1393 | try self.emitConstant(case.values[0].value, target_ty); |
| 1378 | .op = .ne, // not equal, because we want to jump out of this block if it does not match the condition. | 1394 | const opcode = buildOpcode(.{ |
| 1379 | .signedness = signedness, | 1395 | .valtype1 = try self.typeToValtype(target_ty), |
| 1380 | }); | 1396 | .op = .ne, // not equal, because we want to jump out of this block if it does not match the condition. |
| 1381 | try self.code.append(wasm.opcode(opcode)); | 1397 | .signedness = signedness, |
| 1382 | try self.code.append(wasm.opcode(.br_if)); | 1398 | }); |
| 1383 | try leb.writeULEB128(self.code.writer(), @as(u32, 0)); | 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 | } |
| 1384 | } | 1422 | } |
| 1385 | try self.genBody(val.body); | 1423 | try self.genBody(case.body); |
| 1386 | try self.endBlock(); | 1424 | try self.endBlock(); |
| 1387 | } | 1425 | } |
| 1388 | | 1426 | |
| ... | @@ -1390,7 +1428,6 @@ pub const Context = struct { | ... | @@ -1390,7 +1428,6 @@ pub const Context = struct { |
| 1390 | try self.genBody(else_body); | 1428 | try self.genBody(else_body); |
| 1391 | try self.endBlock(); | 1429 | try self.endBlock(); |
| 1392 | } | 1430 | } |
| 1393 | | | |
| 1394 | return .none; | 1431 | return .none; |
| 1395 | } | 1432 | } |
| 1396 | | 1433 | |