From ad38fc11470ad3c2e063ad5739d4f4e84dd495f1 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Thu, 22 Jul 2021 17:51:23 +0200 Subject: 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. --- src/codegen/wasm.zig | 115 ++++++++++++++++++++++++++++----------------------- 1 file changed, 64 insertions(+), 51 deletions(-) (limited to 'src/codegen') diff --git a/src/codegen/wasm.zig b/src/codegen/wasm.zig index 6e469ec769..a8dbf300e3 100644 --- a/src/codegen/wasm.zig +++ b/src/codegen/wasm.zig @@ -1271,60 +1271,73 @@ pub const Context = struct { } fn airSwitchBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue { - const pl_op = self.air.instructions.items(.data)[inst].pl_op; - const extra = self.air.extraData(Air.SwitchBr, pl_op.payload); - const cases = self.air.extra[extra.end..][0..extra.data.cases_len]; - const else_body = self.air.extra[extra.end + cases.len ..][0..extra.data.else_body_len]; - - const target = self.resolveInst(pl_op.operand); - const target_ty = self.air.typeOf(pl_op.operand); - const valtype = try self.typeToValtype(target_ty); // result type is always 'noreturn' const blocktype = wasm.block_empty; + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const target = self.resolveInst(pl_op.operand); + const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload); + var extra_index: usize = switch_br.end; + var case_i: u32 = 0; + + // a map that maps each value with its index and body + var map = std.AutoArrayHashMap(u32, struct { index: u32, body: []const Air.Inst.Index }).init(self.gpa); + defer map.deinit(); + + var lowest: u32 = 0; + var highest: u32 = 0; + while (case_i < switch_br.data.cases_len) : (case_i += 1) { + const case = self.air.extraData(Air.SwitchBr.Case, extra_index); + const items = @bitCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]); + const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len]; + extra_index = case.end + items.len + case_body.len; + + for (items) |ref| { + const item_val = @intCast(u32, self.air.value(ref).?.toUnsignedInt()); + if (item_val < lowest) { + lowest = item_val; + } + if (item_val > highest) { + highest = item_val; + } + try map.put(item_val, .{ .index = case_i, .body = case_body }); + } + + try self.startBlock(.block, blocktype, null); + } - _ = valtype; - _ = blocktype; - _ = target; - _ = else_body; - return self.fail("TODO implement wasm codegen for switch", .{}); - //const signedness: std.builtin.Signedness = blk: { - // // by default we tell the operand type is unsigned (i.e. bools and enum values) - // if (target_ty.zigTypeTag() != .Int) break :blk .unsigned; - - // // incase of an actual integer, we emit the correct signedness - // break :blk target_ty.intInfo(self.target).signedness; - //}; - //for (cases) |case_idx| { - // const case = self.air.extraData(Air.SwitchBr.Case, case_idx); - // const case_body = self.air.extra[case.end..][0..case.data.body_len]; - - // // create a block for each case, when the condition does not match we break out of it - // try self.startBlock(.block, blocktype, null); - // try self.emitWValue(target); - - // const val = self.air.value(case.data.item).?; - // try self.emitConstant(val, target_ty); - // const opcode = buildOpcode(.{ - // .valtype1 = valtype, - // .op = .ne, // not equal because we jump out the block if it does not match the condition - // .signedness = signedness, - // }); - // try self.code.append(wasm.opcode(opcode)); - // try self.code.append(wasm.opcode(.br_if)); - // try leb.writeULEB128(self.code.writer(), @as(u32, 0)); - - // // emit our block code - // try self.genBody(case_body); - - // // end the block we created earlier - // try self.endBlock(); - //} - - //// finally, emit the else case if it exists. Here we will not have to - //// check for a condition, so also no need to emit a block. - //try self.genBody(else_body); - - //return .none; + const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len]; + if (else_body.len != 0) { + try self.startBlock(.block, blocktype, null); + } + + // Generate the jump table 'br_table'. + // The value 'target' represents the index into the table. + // Each index in the table represents a label to the branch + // to jump to. + try self.startBlock(.block, blocktype, null); + try self.emitWValue(target); + try self.code.append(wasm.opcode(.br_table)); + try leb.writeULEB128(self.code.writer(), highest - lowest + 1); + while (lowest <= highest) : (lowest += 1) { + const idx = if (map.get(lowest)) |value| blk: { + break :blk value.index + 1; + } else 0; + try leb.writeULEB128(self.code.writer(), idx); + } else if (else_body.len != 0) { + try leb.writeULEB128(self.code.writer(), @as(u32, 0)); // default branch + } + try self.endBlock(); + + if (else_body.len != 0) { + try self.genBody(else_body); + try self.endBlock(); + } + + for (map.values()) |val| { + try self.genBody(val.body); + try self.endBlock(); + } + return .none; } fn airIsErr(self: *Context, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue { -- cgit v1.2.3 From cb41f0e58d4788806aac0bfece2e298c3c79b737 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Fri, 23 Jul 2021 16:41:04 +0200 Subject: switchbr: When prongs are sparse values, use if/else-chain --- src/codegen/wasm.zig | 97 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 69 insertions(+), 28 deletions(-) (limited to 'src/codegen') diff --git a/src/codegen/wasm.zig b/src/codegen/wasm.zig index a8dbf300e3..cada1fa24b 100644 --- a/src/codegen/wasm.zig +++ b/src/codegen/wasm.zig @@ -1275,12 +1275,17 @@ pub const Context = struct { const blocktype = wasm.block_empty; const pl_op = self.air.instructions.items(.data)[inst].pl_op; const target = self.resolveInst(pl_op.operand); + const target_ty = self.air.typeOf(pl_op.operand); const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload); var extra_index: usize = switch_br.end; var case_i: u32 = 0; // a map that maps each value with its index and body - var map = std.AutoArrayHashMap(u32, struct { index: u32, body: []const Air.Inst.Index }).init(self.gpa); + var map = std.AutoArrayHashMap(u32, struct { + index: u32, + body: []const Air.Inst.Index, + value: Value, + }).init(self.gpa); defer map.deinit(); var lowest: u32 = 0; @@ -1292,51 +1297,87 @@ pub const Context = struct { extra_index = case.end + items.len + case_body.len; for (items) |ref| { - const item_val = @intCast(u32, self.air.value(ref).?.toUnsignedInt()); - if (item_val < lowest) { - lowest = item_val; + const item_val = self.air.value(ref).?; + // safe to truncate the values as we only use them when + // the target's bits is 32 or lower. + const int_val = @truncate(u32, item_val.toUnsignedInt()); + if (int_val < lowest) { + lowest = int_val; } - if (item_val > highest) { - highest = item_val; + if (int_val > highest) { + highest = int_val; } - try map.put(item_val, .{ .index = case_i, .body = case_body }); + try map.put(int_val, .{ .index = case_i, .body = case_body, .value = item_val }); } try self.startBlock(.block, blocktype, null); } + // When the highest and lowest values are seperated by '50', + // we define it as sparse and use an if/else-chain, rather than a jump table. + // When the target is an integer size larger than u32, we have no way to use the value + // as an index, therefore we also use an if/else-chain for those cases. + // TODO: Benchmark this to find a proper value, LLVM seems to draw the line at '40~45'. + const is_sparse = target_ty.intInfo(self.target).bits > 32 or highest - lowest > 50; + const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len]; - if (else_body.len != 0) { + const has_else_body = else_body.len != 0; + if (has_else_body) { try self.startBlock(.block, blocktype, null); } - // Generate the jump table 'br_table'. - // The value 'target' represents the index into the table. - // Each index in the table represents a label to the branch - // to jump to. - try self.startBlock(.block, blocktype, null); - try self.emitWValue(target); - try self.code.append(wasm.opcode(.br_table)); - try leb.writeULEB128(self.code.writer(), highest - lowest + 1); - while (lowest <= highest) : (lowest += 1) { - const idx = if (map.get(lowest)) |value| blk: { - break :blk value.index + 1; - } else 0; - try leb.writeULEB128(self.code.writer(), idx); - } else if (else_body.len != 0) { - try leb.writeULEB128(self.code.writer(), @as(u32, 0)); // default branch - } - try self.endBlock(); - - if (else_body.len != 0) { - try self.genBody(else_body); + if (!is_sparse) { + // Generate the jump table 'br_table' when the prongs are not sparse. + // The value 'target' represents the index into the table. + // Each index in the table represents a label to the branch + // to jump to. + try self.startBlock(.block, blocktype, null); + try self.emitWValue(target); + try self.code.append(wasm.opcode(.br_table)); + const depth = highest - lowest + @boolToInt(has_else_body); + try leb.writeULEB128(self.code.writer(), depth); + while (lowest <= highest) : (lowest += 1) { + const idx = if (map.get(lowest)) |value| blk: { + break :blk value.index; + } else if (has_else_body) case_i else unreachable; + try leb.writeULEB128(self.code.writer(), idx); + } else if (has_else_body) { + try leb.writeULEB128(self.code.writer(), @as(u32, case_i)); // default branch + } try self.endBlock(); } + const signedness: std.builtin.Signedness = blk: { + // by default we tell the operand type is unsigned (i.e. bools and enum values) + if (target_ty.zigTypeTag() != .Int) break :blk .unsigned; + + // incase of an actual integer, we emit the correct signedness + break :blk target_ty.intInfo(self.target).signedness; + }; + for (map.values()) |val| { + // when sparse, we use if/else-chain, so emit conditional checks + if (is_sparse) { + try self.emitWValue(target); + try self.emitConstant(val.value, target_ty); + const opcode = buildOpcode(.{ + .valtype1 = try self.typeToValtype(target_ty), + .op = .ne, // not equal, because we want to jump out of this block if it does not match the condition. + .signedness = signedness, + }); + try self.code.append(wasm.opcode(opcode)); + try self.code.append(wasm.opcode(.br_if)); + try leb.writeULEB128(self.code.writer(), @as(u32, 0)); + } try self.genBody(val.body); try self.endBlock(); } + + if (has_else_body) { + try self.genBody(else_body); + try self.endBlock(); + } + return .none; } -- cgit v1.2.3 From 72149ae7e4a826d1657d82c367596e06ff771734 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Fri, 23 Jul 2021 22:31:45 +0200 Subject: Allow negative values --- src/codegen/wasm.zig | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) (limited to 'src/codegen') diff --git a/src/codegen/wasm.zig b/src/codegen/wasm.zig index cada1fa24b..5d5d4e1c2b 100644 --- a/src/codegen/wasm.zig +++ b/src/codegen/wasm.zig @@ -1281,15 +1281,15 @@ pub const Context = struct { var case_i: u32 = 0; // a map that maps each value with its index and body - var map = std.AutoArrayHashMap(u32, struct { + var map = std.AutoArrayHashMap(i32, struct { index: u32, body: []const Air.Inst.Index, value: Value, }).init(self.gpa); defer map.deinit(); - var lowest: u32 = 0; - var highest: u32 = 0; + var lowest: i32 = 0; + var highest: i32 = 0; while (case_i < switch_br.data.cases_len) : (case_i += 1) { const case = self.air.extraData(Air.SwitchBr.Case, extra_index); const items = @bitCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]); @@ -1298,9 +1298,15 @@ pub const Context = struct { for (items) |ref| { const item_val = self.air.value(ref).?; - // safe to truncate the values as we only use them when - // the target's bits is 32 or lower. - const int_val = @truncate(u32, item_val.toUnsignedInt()); + const int_val: i32 = blk: { + if (target_ty.intInfo(self.target).signedness == .signed) { + // safe to truncate the values as we only use them when + // the target's bits is 32 or lower. + break :blk @truncate(i32, item_val.toSignedInt()); + } + + break :blk @intCast(i32, @truncate(u31, item_val.toUnsignedInt()) - 1); + }; if (int_val < lowest) { lowest = int_val; } @@ -1333,9 +1339,16 @@ pub const Context = struct { // to jump to. try self.startBlock(.block, blocktype, null); try self.emitWValue(target); + if (lowest < 0) { + // since br_table works using indexes, starting from '0', we must ensure all values + // we put inside, are atleast 0. + try self.code.append(wasm.opcode(.i32_const)); + try leb.writeILEB128(self.code.writer(), lowest * -1); + try self.code.append(wasm.opcode(.i32_add)); + } try self.code.append(wasm.opcode(.br_table)); const depth = highest - lowest + @boolToInt(has_else_body); - try leb.writeULEB128(self.code.writer(), depth); + try leb.writeILEB128(self.code.writer(), depth); while (lowest <= highest) : (lowest += 1) { const idx = if (map.get(lowest)) |value| blk: { break :blk value.index; -- cgit v1.2.3 From 5d98abd5703607884844e8b8165dcc2238389c57 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sat, 24 Jul 2021 17:50:26 +0200 Subject: Support multi-value prongs --- src/codegen/wasm.zig | 91 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 64 insertions(+), 27 deletions(-) (limited to 'src/codegen') diff --git a/src/codegen/wasm.zig b/src/codegen/wasm.zig index 5d5d4e1c2b..43401c9767 100644 --- a/src/codegen/wasm.zig +++ b/src/codegen/wasm.zig @@ -979,10 +979,15 @@ pub const Context = struct { .valtype1 = try self.typeToValtype(ty), }); try writer.writeByte(wasm.opcode(opcode)); + const int_info = ty.intInfo(self.target); // write constant - switch (ty.intInfo(self.target).signedness) { + switch (int_info.signedness) { .signed => try leb.writeILEB128(writer, value.toSignedInt()), - .unsigned => try leb.writeILEB128(writer, value.toUnsignedInt()), + .unsigned => switch (int_info.bits) { + 0...32 => try leb.writeILEB128(writer, @bitCast(i32, @intCast(u32, value.toUnsignedInt()))), + 33...64 => try leb.writeILEB128(writer, @bitCast(i64, value.toUnsignedInt())), + else => |bits| return self.fail("Wasm TODO: emitConstant for integer with {d} bits", .{bits}), + }, } }, .Bool => { @@ -1280,13 +1285,15 @@ pub const Context = struct { var extra_index: usize = switch_br.end; var case_i: u32 = 0; - // a map that maps each value with its index and body - var map = std.AutoArrayHashMap(i32, struct { - index: u32, + // a list that maps each value with its value and body based on the order inside the list. + const CaseValue = struct { integer: i32, value: Value }; + var case_list = try std.ArrayList(struct { + values: []const CaseValue, body: []const Air.Inst.Index, - value: Value, - }).init(self.gpa); - defer map.deinit(); + }).initCapacity(self.gpa, switch_br.data.cases_len); + defer for (case_list.items) |case| { + self.gpa.free(case.values); + } else case_list.deinit(); var lowest: i32 = 0; var highest: i32 = 0; @@ -1295,8 +1302,10 @@ pub const Context = struct { const items = @bitCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]); const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len]; extra_index = case.end + items.len + case_body.len; + const values = try self.gpa.alloc(CaseValue, items.len); + errdefer self.gpa.free(values); - for (items) |ref| { + for (items) |ref, i| { const item_val = self.air.value(ref).?; const int_val: i32 = blk: { if (target_ty.intInfo(self.target).signedness == .signed) { @@ -1305,7 +1314,7 @@ pub const Context = struct { break :blk @truncate(i32, item_val.toSignedInt()); } - break :blk @intCast(i32, @truncate(u31, item_val.toUnsignedInt()) - 1); + break :blk @bitCast(i32, @truncate(u32, item_val.toUnsignedInt())); }; if (int_val < lowest) { lowest = int_val; @@ -1313,9 +1322,10 @@ pub const Context = struct { if (int_val > highest) { highest = int_val; } - try map.put(int_val, .{ .index = case_i, .body = case_body, .value = item_val }); + values[i] = .{ .integer = int_val, .value = item_val }; } + case_list.appendAssumeCapacity(.{ .values = values, .body = case_body }); try self.startBlock(.block, blocktype, null); } @@ -1350,9 +1360,15 @@ pub const Context = struct { const depth = highest - lowest + @boolToInt(has_else_body); try leb.writeILEB128(self.code.writer(), depth); while (lowest <= highest) : (lowest += 1) { - const idx = if (map.get(lowest)) |value| blk: { - break :blk value.index; - } else if (has_else_body) case_i else unreachable; + // idx represents the branch we jump to + const idx = blk: { + for (case_list.items) |case, idx| { + for (case.values) |case_value| { + if (case_value.integer == lowest) break :blk @intCast(u32, idx); + } + } + break :blk if (has_else_body) case_i else unreachable; + }; try leb.writeULEB128(self.code.writer(), idx); } else if (has_else_body) { try leb.writeULEB128(self.code.writer(), @as(u32, case_i)); // default branch @@ -1368,21 +1384,43 @@ pub const Context = struct { break :blk target_ty.intInfo(self.target).signedness; }; - for (map.values()) |val| { + for (case_list.items) |case| { // when sparse, we use if/else-chain, so emit conditional checks if (is_sparse) { - try self.emitWValue(target); - try self.emitConstant(val.value, target_ty); - const opcode = buildOpcode(.{ - .valtype1 = try self.typeToValtype(target_ty), - .op = .ne, // not equal, because we want to jump out of this block if it does not match the condition. - .signedness = signedness, - }); - try self.code.append(wasm.opcode(opcode)); - try self.code.append(wasm.opcode(.br_if)); - try leb.writeULEB128(self.code.writer(), @as(u32, 0)); + // for single value prong we can emit a simple if + if (case.values.len == 1) { + try self.emitWValue(target); + try self.emitConstant(case.values[0].value, target_ty); + const opcode = buildOpcode(.{ + .valtype1 = try self.typeToValtype(target_ty), + .op = .ne, // not equal, because we want to jump out of this block if it does not match the condition. + .signedness = signedness, + }); + try self.code.append(wasm.opcode(opcode)); + try self.code.append(wasm.opcode(.br_if)); + try leb.writeULEB128(self.code.writer(), @as(u32, 0)); + } else { + // in multi-value prongs we must check if any prongs match the target value. + try self.startBlock(.block, blocktype, null); + for (case.values) |value| { + try self.emitWValue(target); + try self.emitConstant(value.value, target_ty); + const opcode = buildOpcode(.{ + .valtype1 = try self.typeToValtype(target_ty), + .op = .eq, + .signedness = signedness, + }); + try self.code.append(wasm.opcode(opcode)); + try self.code.append(wasm.opcode(.br_if)); + try leb.writeULEB128(self.code.writer(), @as(u32, 0)); + } + // value did not match any of the prong values + try self.code.append(wasm.opcode(.br)); + try leb.writeULEB128(self.code.writer(), @as(u32, 1)); + try self.endBlock(); + } } - try self.genBody(val.body); + try self.genBody(case.body); try self.endBlock(); } @@ -1390,7 +1428,6 @@ pub const Context = struct { try self.genBody(else_body); try self.endBlock(); } - return .none; } -- cgit v1.2.3 From 30376a82b2c1b13047ff3b48391fcda44183e129 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sat, 24 Jul 2021 19:48:55 +0200 Subject: Re-enable switch test cases and fix regressions --- src/codegen/wasm.zig | 48 +++++++++++++++----- test/stage2/wasm.zig | 122 +++++++++++++++++++++++++-------------------------- 2 files changed, 98 insertions(+), 72 deletions(-) (limited to 'src/codegen') diff --git a/src/codegen/wasm.zig b/src/codegen/wasm.zig index 43401c9767..ca0d53988d 100644 --- a/src/codegen/wasm.zig +++ b/src/codegen/wasm.zig @@ -1084,6 +1084,42 @@ pub const Context = struct { } } + /// Returns a `Value` as a signed 32 bit value. + /// It's illegale to provide a value with a type that cannot be represented + /// as an integer value. + fn valueAsI32(self: Context, val: Value, ty: Type) i32 { + switch (ty.zigTypeTag()) { + .Enum => { + if (val.castTag(.enum_field_index)) |field_index| { + switch (ty.tag()) { + .enum_simple => return @bitCast(i32, field_index.data), + .enum_full, .enum_nonexhaustive => { + const enum_full = ty.cast(Type.Payload.EnumFull).?.data; + if (enum_full.values.count() != 0) { + const tag_val = enum_full.values.keys()[field_index.data]; + return self.valueAsI32(tag_val, enum_full.tag_ty); + } else return @bitCast(i32, field_index.data); + }, + else => unreachable, + } + } else { + var int_tag_buffer: Type.Payload.Bits = undefined; + const int_tag_ty = ty.intTagType(&int_tag_buffer); + return self.valueAsI32(val, int_tag_ty); + } + }, + .Int => switch (ty.intInfo(self.target).signedness) { + .signed => return @truncate(i32, val.toSignedInt()), + .unsigned => return @bitCast(i32, @truncate(u32, val.toUnsignedInt())), + }, + .ErrorSet => { + const error_index = self.global_error_set.get(val.getError().?).?; + return @bitCast(i32, error_index); + }, + else => unreachable, // Programmer called this function for an illegal type + } + } + fn airBlock(self: *Context, inst: Air.Inst.Index) InnerError!WValue { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const block_ty = try self.genBlockType(self.air.getRefType(ty_pl.ty)); @@ -1307,15 +1343,7 @@ pub const Context = struct { for (items) |ref, i| { const item_val = self.air.value(ref).?; - const int_val: i32 = blk: { - if (target_ty.intInfo(self.target).signedness == .signed) { - // safe to truncate the values as we only use them when - // the target's bits is 32 or lower. - break :blk @truncate(i32, item_val.toSignedInt()); - } - - break :blk @bitCast(i32, @truncate(u32, item_val.toUnsignedInt())); - }; + const int_val = self.valueAsI32(item_val, target_ty); if (int_val < lowest) { lowest = int_val; } @@ -1334,7 +1362,7 @@ pub const Context = struct { // When the target is an integer size larger than u32, we have no way to use the value // as an index, therefore we also use an if/else-chain for those cases. // TODO: Benchmark this to find a proper value, LLVM seems to draw the line at '40~45'. - const is_sparse = target_ty.intInfo(self.target).bits > 32 or highest - lowest > 50; + const is_sparse = highest - lowest > 50 or target_ty.bitSize(self.target) > 32; const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len]; const has_else_body = else_body.len != 0; diff --git a/test/stage2/wasm.zig b/test/stage2/wasm.zig index a43c4e5de3..f746be99d2 100644 --- a/test/stage2/wasm.zig +++ b/test/stage2/wasm.zig @@ -479,68 +479,66 @@ pub fn addCases(ctx: *TestContext) !void { , "30\n"); } - // This test case is disabled until the codegen for switch is reworked - // to take advantage of br_table rather than a series of br_if opcodes. - //{ - // var case = ctx.exe("wasm switch", wasi); - - // case.addCompareOutput( - // \\pub export fn _start() u32 { - // \\ var val: u32 = 1; - // \\ var a: u32 = switch (val) { - // \\ 0, 1 => 2, - // \\ 2 => 3, - // \\ 3 => 4, - // \\ else => 5, - // \\ }; - // \\ - // \\ return a; - // \\} - // , "2\n"); - - // case.addCompareOutput( - // \\pub export fn _start() u32 { - // \\ var val: u32 = 2; - // \\ var a: u32 = switch (val) { - // \\ 0, 1 => 2, - // \\ 2 => 3, - // \\ 3 => 4, - // \\ else => 5, - // \\ }; - // \\ - // \\ return a; - // \\} - // , "3\n"); - - // case.addCompareOutput( - // \\pub export fn _start() u32 { - // \\ var val: u32 = 10; - // \\ var a: u32 = switch (val) { - // \\ 0, 1 => 2, - // \\ 2 => 3, - // \\ 3 => 4, - // \\ else => 5, - // \\ }; - // \\ - // \\ return a; - // \\} - // , "5\n"); - - // case.addCompareOutput( - // \\const MyEnum = enum { One, Two, Three }; - // \\ - // \\pub export fn _start() u32 { - // \\ var val: MyEnum = .Two; - // \\ var a: u32 = switch (val) { - // \\ .One => 1, - // \\ .Two => 2, - // \\ .Three => 3, - // \\ }; - // \\ - // \\ return a; - // \\} - // , "2\n"); - //} + { + var case = ctx.exe("wasm switch", wasi); + + case.addCompareOutput( + \\pub export fn _start() u32 { + \\ var val: u32 = 1; + \\ var a: u32 = switch (val) { + \\ 0, 1 => 2, + \\ 2 => 3, + \\ 3 => 4, + \\ else => 5, + \\ }; + \\ + \\ return a; + \\} + , "2\n"); + + case.addCompareOutput( + \\pub export fn _start() u32 { + \\ var val: u32 = 2; + \\ var a: u32 = switch (val) { + \\ 0, 1 => 2, + \\ 2 => 3, + \\ 3 => 4, + \\ else => 5, + \\ }; + \\ + \\ return a; + \\} + , "3\n"); + + case.addCompareOutput( + \\pub export fn _start() u32 { + \\ var val: u32 = 10; + \\ var a: u32 = switch (val) { + \\ 0, 1 => 2, + \\ 2 => 3, + \\ 3 => 4, + \\ else => 5, + \\ }; + \\ + \\ return a; + \\} + , "5\n"); + + case.addCompareOutput( + \\const MyEnum = enum { One, Two, Three }; + \\ + \\pub export fn _start() u32 { + \\ var val: MyEnum = .Two; + \\ var a: u32 = switch (val) { + \\ .One => 1, + \\ .Two => 2, + \\ .Three => 3, + \\ }; + \\ + \\ return a; + \\} + , "2\n"); + } { var case = ctx.exe("wasm error unions", wasi); -- cgit v1.2.3