authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-07-16 22:43:06+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-20 12:19:16-07:00
log2438f61f1c37aefa16852130370df44b3fabf785
tree831f7d890c87cf50119f5f639a2818e9a7b1c8ba
parent424f260f850cb22637888bbfdf5bfaf9c08a4dae

Refactor entire wasm-backend to use new AIR memory layout


2 files changed, 161 insertions(+), 116 deletions(-)

src/codegen/wasm.zig+160-115
......@@ -483,8 +483,8 @@ pub const Result = union(enum) {
483483 externally_managed: []const u8,
484484};
485485
486/// Hashmap to store generated `WValue` for each `Inst`
487pub const ValueTable = std.AutoHashMapUnmanaged(Air.Inst.Index, WValue);
486/// Hashmap to store generated `WValue` for each `Air.Inst.Ref`
487pub const ValueTable = std.AutoHashMapUnmanaged(Air.Inst.Ref, WValue);
488488
489489/// Code represents the `Code` section of wasm that
490490/// belongs to a function
......@@ -495,7 +495,7 @@ pub const Context = struct {
495495 air: Air,
496496 liveness: Liveness,
497497 gpa: *mem.Allocator,
498 /// Table to save `WValue`'s generated by an `Inst`
498 /// Table to save `WValue`'s generated by an `Air.Inst`
499499 values: ValueTable,
500500 /// Mapping from Air.Inst.Index to block ids
501501 blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, u32) = .{},
......@@ -547,14 +547,15 @@ pub const Context = struct {
547547
548548 /// Resolves the `WValue` for the given instruction `inst`
549549 /// When the given instruction has a `Value`, it returns a constant instead
550 fn resolveInst(self: Context, inst: Air.Inst) Index {
551 if (!inst.ty.hasCodeGenBits()) return .none;
550 fn resolveInst(self: Context, ref: Air.Inst.Ref) WValue {
551 const ref_type = self.air.getRefType(ref);
552 if (ref_type.hasCodeGenBits()) return .none;
552553
553 if (inst.value()) |_| {
554 return WValue{ .constant = inst };
554 if (self.air.instructions.items(.tag)[@enumToInt(ref)] == .constant) {
555 return WValue{ .constant = @enumToInt(ref) };
555556 }
556557
557 return self.values.get(inst).?; // Instruction does not dominate all uses!
558 return self.values.get(ref).?; // Instruction does not dominate all uses!
558559 }
559560
560561 /// Using a given `Type`, returns the corresponding wasm Valtype
......@@ -610,7 +611,12 @@ pub const Context = struct {
610611 try writer.writeByte(wasm.opcode(.local_get));
611612 try leb.writeULEB128(writer, idx);
612613 },
613 .constant => |inst| try self.emitConstant(inst.value().?, inst.ty), // creates a new constant onto the stack
614 .constant => |index| {
615 const ty_pl = self.air.instructions.items(.data)[index].ty_pl;
616 const value = self.air.values[ty_pl.payload];
617 // create a new constant onto the stack
618 try self.emitConstant(value, self.air.getRefType(ty_pl.ty));
619 },
614620 }
615621 }
616622
......@@ -626,10 +632,7 @@ pub const Context = struct {
626632 const fields_len = @intCast(u32, struct_data.fields.count());
627633 try self.locals.ensureCapacity(self.gpa, self.locals.items.len + fields_len);
628634 for (struct_data.fields.values()) |*value| {
629 const val_type = try self.genValtype(
630 .{ .node_offset = struct_data.node_offset },
631 value.ty,
632 );
635 const val_type = try self.genValtype(value.ty);
633636 self.locals.appendAssumeCapacity(val_type);
634637 self.local_index += 1;
635638 }
......@@ -640,7 +643,7 @@ pub const Context = struct {
640643 },
641644 .ErrorUnion => {
642645 const payload_type = ty.errorUnionChild();
643 const val_type = try self.genValtype(.{ .node_offset = 0 }, payload_type);
646 const val_type = try self.genValtype(payload_type);
644647
645648 // we emit the error value as the first local, and the payload as the following.
646649 // The first local is also used to find the index of the error and payload.
......@@ -657,7 +660,7 @@ pub const Context = struct {
657660 } };
658661 },
659662 else => {
660 const valtype = try self.genValtype(.{ .node_offset = 0 }, ty);
663 const valtype = try self.genValtype(ty);
661664 try self.locals.append(self.gpa, valtype);
662665 self.local_index += 1;
663666 return WValue{ .local = initial_index };
......@@ -708,8 +711,7 @@ pub const Context = struct {
708711 }
709712 }
710713
711 pub fn genFunc(self: *Context, func: *Module.Fn) InnerError!Result {
712 _ = func;
714 pub fn genFunc(self: *Context) InnerError!Result {
713715 try self.genFunctype();
714716 // TODO: check for and handle death of instructions
715717
......@@ -790,44 +792,43 @@ pub const Context = struct {
790792 fn genInst(self: *Context, inst: Air.Inst.Index) !WValue {
791793 const air_tags = self.air.instructions.items(.tag);
792794 return switch (air_tags[inst]) {
793 // .add => self.genBinOp(inst.castTag(.add).?, .add),
794 // .alloc => self.genAlloc(inst.castTag(.alloc).?),
795 // .arg => self.genArg(inst.castTag(.arg).?),
796 // .bit_and => self.genBinOp(inst.castTag(.bit_and).?, .@"and"),
797 // .bitcast => self.genBitcast(inst.castTag(.bitcast).?),
798 // .bit_or => self.genBinOp(inst.castTag(.bit_or).?, .@"or"),
799 // .block => self.genBlock(inst.castTag(.block).?),
800 // .bool_and => self.genBinOp(inst.castTag(.bool_and).?, .@"and"),
801 // .bool_or => self.genBinOp(inst.castTag(.bool_or).?, .@"or"),
802 // .breakpoint => self.genBreakpoint(inst.castTag(.breakpoint).?),
803 // .br => self.genBr(inst.castTag(.br).?),
804 // .call => self.genCall(inst.castTag(.call).?),
805 // .cmp_eq => self.genCmp(inst.castTag(.cmp_eq).?, .eq),
806 // .cmp_gte => self.genCmp(inst.castTag(.cmp_gte).?, .gte),
807 // .cmp_gt => self.genCmp(inst.castTag(.cmp_gt).?, .gt),
808 // .cmp_lte => self.genCmp(inst.castTag(.cmp_lte).?, .lte),
809 // .cmp_lt => self.genCmp(inst.castTag(.cmp_lt).?, .lt),
810 // .cmp_neq => self.genCmp(inst.castTag(.cmp_neq).?, .neq),
811 // .condbr => self.genCondBr(inst.castTag(.condbr).?),
812 // .constant => unreachable,
813 // .dbg_stmt => WValue.none,
814 // .div => self.genBinOp(inst.castTag(.div).?, .div),
815 // .is_err => self.genIsErr(inst.castTag(.is_err).?, .i32_ne),
816 // .is_non_err => self.genIsErr(inst.castTag(.is_non_err).?, .i32_eq),
817 // .load => self.genLoad(inst.castTag(.load).?),
818 // .loop => self.genLoop(inst.castTag(.loop).?),
819 // .mul => self.genBinOp(inst.castTag(.mul).?, .mul),
820 // .not => self.genNot(inst.castTag(.not).?),
821 // .ret => self.genRet(inst.castTag(.ret).?),
822 // .retvoid => WValue.none,
823 // .store => self.genStore(inst.castTag(.store).?),
824 // .struct_field_ptr => self.genStructFieldPtr(inst.castTag(.struct_field_ptr).?),
825 // .sub => self.genBinOp(inst.castTag(.sub).?, .sub),
826 // .switchbr => self.genSwitchBr(inst.castTag(.switchbr).?),
827 // .unreach => self.genUnreachable(inst.castTag(.unreach).?),
828 // .unwrap_errunion_payload => self.genUnwrapErrUnionPayload(inst.castTag(.unwrap_errunion_payload).?),
829 // .wrap_errunion_payload => self.genWrapErrUnionPayload(inst.castTag(.wrap_errunion_payload).?),
830 // .xor => self.genBinOp(inst.castTag(.xor).?, .xor),
795 .add => self.genBinOp(inst, .add),
796 .alloc => self.genAlloc(inst),
797 .arg => self.genArg(inst),
798 .bit_and => self.genBinOp(inst, .@"and"),
799 .bitcast => self.genBitcast(inst),
800 .bit_or => self.genBinOp(inst, .@"or"),
801 .block => self.genBlock(inst),
802 .bool_and => self.genBinOp(inst, .@"and"),
803 .bool_or => self.genBinOp(inst, .@"or"),
804 .breakpoint => self.genBreakpoint(inst),
805 .br => self.genBr(inst),
806 .call => self.genCall(inst),
807 .cmp_eq => self.genCmp(inst, .eq),
808 .cmp_gte => self.genCmp(inst, .gte),
809 .cmp_gt => self.genCmp(inst, .gt),
810 .cmp_lte => self.genCmp(inst, .lte),
811 .cmp_lt => self.genCmp(inst, .lt),
812 .cmp_neq => self.genCmp(inst, .neq),
813 .cond_br => self.genCondBr(inst),
814 .constant => unreachable,
815 .dbg_stmt => WValue.none,
816 .div => self.genBinOp(inst, .div),
817 .is_err => self.genIsErr(inst, .i32_ne),
818 .is_non_err => self.genIsErr(inst, .i32_eq),
819 .load => self.genLoad(inst),
820 .loop => self.genLoop(inst),
821 .mul => self.genBinOp(inst, .mul),
822 .not => self.genNot(inst),
823 .ret => self.genRet(inst),
824 .store => self.genStore(inst),
825 .struct_field_ptr => self.genStructFieldPtr(inst),
826 .sub => self.genBinOp(inst, .sub),
827 .switch_br => self.genSwitchBr(inst),
828 .unreach => self.genUnreachable(inst),
829 .unwrap_errunion_payload => self.genUnwrapErrUnionPayload(inst),
830 .wrap_errunion_payload => self.genWrapErrUnionPayload(inst),
831 .xor => self.genBinOp(inst, .xor),
831832 else => |tag| self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),
832833 };
833834 }
......@@ -835,22 +836,27 @@ pub const Context = struct {
835836 fn genBody(self: *Context, body: []const Air.Inst.Index) InnerError!void {
836837 for (body) |inst| {
837838 const result = try self.genInst(inst);
838 try self.values.putNoClobber(self.gpa, inst, result);
839 try self.values.putNoClobber(self.gpa, @intToEnum(Air.Inst.Ref, inst), result);
839840 }
840841 }
841842
842843 fn genRet(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
843 // TODO: Implement tail calls
844 const operand = self.resolveInst(inst.operand);
844 const un_op = self.air.instructions.items(.data)[inst].un_op;
845 const operand = self.resolveInst(un_op);
845846 try self.emitWValue(operand);
846847 try self.code.append(wasm.opcode(.@"return"));
847848 return .none;
848849 }
849850
850851 fn genCall(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
851 const func_val = inst.func.value().?;
852 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
853 const extra = self.air.extraData(Air.Call, pl_op.payload);
854 const args = self.air.extra[extra.end..][0..extra.data.args_len];
852855
853856 const target: *Decl = blk: {
857 const ty_pl = self.air.instructions.items(.data)[@enumToInt(pl_op.operand)].ty_pl;
858 const func_val = self.air.values[ty_pl.payload];
859
854860 if (func_val.castTag(.function)) |func| {
855861 break :blk func.data.owner_decl;
856862 } else if (func_val.castTag(.extern_fn)) |ext_fn| {
......@@ -859,8 +865,8 @@ pub const Context = struct {
859865 return self.fail("Expected a function, but instead found type '{s}'", .{func_val.tag()});
860866 };
861867
862 for (inst.args) |arg| {
863 const arg_val = self.resolveInst(arg);
868 for (args) |arg| {
869 const arg_val = self.resolveInst(@intToEnum(Air.Inst.Ref, arg));
864870 try self.emitWValue(arg_val);
865871 }
866872
......@@ -877,15 +883,16 @@ pub const Context = struct {
877883 }
878884
879885 fn genAlloc(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
880 const elem_type = inst.base.ty.elemType();
886 const elem_type = self.air.getType(inst).elemType();
881887 return self.allocLocal(elem_type);
882888 }
883889
884890 fn genStore(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
891 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
885892 const writer = self.code.writer();
886893
887 const lhs = self.resolveInst(inst.lhs);
888 const rhs = self.resolveInst(inst.rhs);
894 const lhs = self.resolveInst(bin_op.lhs);
895 const rhs = self.resolveInst(bin_op.rhs);
889896
890897 switch (lhs) {
891898 .multi_value => |multi_value| switch (rhs) {
......@@ -893,7 +900,7 @@ pub const Context = struct {
893900 // we simply assign the local_index to the rhs one.
894901 // This allows us to update struct fields without having to individually
895902 // set each local as each field's index will be calculated off the struct's base index
896 .multi_value => self.values.put(self.gpa, inst.lhs, rhs) catch unreachable, // Instruction does not dominate all uses!
903 .multi_value => self.values.put(self.gpa, bin_op.lhs, rhs) catch unreachable, // Instruction does not dominate all uses!
897904 .constant, .none => {
898905 // emit all values onto the stack if constant
899906 try self.emitWValue(rhs);
......@@ -920,7 +927,8 @@ pub const Context = struct {
920927 }
921928
922929 fn genLoad(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
923 return self.resolveInst(inst.operand);
930 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
931 return self.resolveInst(ty_op.operand);
924932 }
925933
926934 fn genArg(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
......@@ -931,8 +939,9 @@ pub const Context = struct {
931939 }
932940
933941 fn genBinOp(self: *Context, inst: Air.Inst.Index, op: Op) InnerError!WValue {
934 const lhs = self.resolveInst(inst.lhs);
935 const rhs = self.resolveInst(inst.rhs);
942 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
943 const lhs = self.resolveInst(bin_op.lhs);
944 const rhs = self.resolveInst(bin_op.rhs);
936945
937946 // it's possible for both lhs and/or rhs to return an offset as well,
938947 // in which case we return the first offset occurance we find.
......@@ -945,10 +954,11 @@ pub const Context = struct {
945954 try self.emitWValue(lhs);
946955 try self.emitWValue(rhs);
947956
957 const bin_ty = self.air.getRefType(bin_op.lhs);
948958 const opcode: wasm.Opcode = buildOpcode(.{
949959 .op = op,
950 .valtype1 = try self.typeToValtype(inst.base.ty),
951 .signedness = if (inst.base.ty.isSignedInt()) .signed else .unsigned,
960 .valtype1 = try self.typeToValtype(bin_ty),
961 .signedness = if (bin_ty.isSignedInt()) .signed else .unsigned,
952962 });
953963 try self.code.append(wasm.opcode(opcode));
954964 return WValue{ .code_offset = offset };
......@@ -1064,14 +1074,17 @@ pub const Context = struct {
10641074 }
10651075 }
10661076
1067 fn genBlock(self: *Context, block: Air.Inst.Index) InnerError!WValue {
1068 const block_ty = try self.genBlockType(block.base.ty);
1077 fn genBlock(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1078 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1079 const block_ty = try self.genBlockType(self.air.getRefType(ty_pl.ty));
1080 const extra = self.air.extraData(Air.Block, ty_pl.payload);
1081 const body = self.air.extra[extra.end..][0..extra.data.body_len];
10691082
10701083 try self.startBlock(.block, block_ty, null);
10711084 // Here we set the current block idx, so breaks know the depth to jump
10721085 // to when breaking out.
1073 try self.blocks.putNoClobber(self.gpa, block, self.block_depth);
1074 try self.genBody(block.body);
1086 try self.blocks.putNoClobber(self.gpa, inst, self.block_depth);
1087 try self.genBody(body);
10751088 try self.endBlock();
10761089
10771090 return .none;
......@@ -1095,11 +1108,15 @@ pub const Context = struct {
10951108 self.block_depth -= 1;
10961109 }
10971110
1098 fn genLoop(self: *Context, loop: Air.Inst.Index) InnerError!WValue {
1099 const loop_ty = try self.genBlockType(loop.base.ty);
1111 fn genLoop(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1112 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1113 const loop = self.air.extraData(Air.Block, ty_pl.payload);
1114 const body = self.air.extra[loop.end..][0..loop.data.body_len];
11001115
1101 try self.startBlock(.loop, loop_ty, null);
1102 try self.genBody(loop.body);
1116 // result type of loop is always 'noreturn', meaning we can always
1117 // emit the wasm type 'block_empty'.
1118 try self.startBlock(.loop, wasm.block_empty, null);
1119 try self.genBody(body);
11031120
11041121 // breaking to the index of a loop block will continue the loop instead
11051122 try self.code.append(wasm.opcode(.br));
......@@ -1110,8 +1127,12 @@ pub const Context = struct {
11101127 return .none;
11111128 }
11121129
1113 fn genCondBr(self: *Context, condbr: Air.Inst.Index) InnerError!WValue {
1114 const condition = self.resolveInst(condbr.condition);
1130 fn genCondBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1131 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
1132 const condition = self.resolveInst(pl_op.operand);
1133 const extra = self.air.extraData(Air.CondBr, pl_op.payload);
1134 const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];
1135 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
11151136 const writer = self.code.writer();
11161137
11171138 // TODO: Handle death instructions for then and else body
......@@ -1126,8 +1147,9 @@ pub const Context = struct {
11261147 break :blk offset;
11271148 },
11281149 };
1129 const block_ty = try self.genBlockType(condbr.base.ty);
1130 try self.startBlock(.block, block_ty, offset);
1150
1151 // result type is always noreturn, so use `block_empty` as type.
1152 try self.startBlock(.block, wasm.block_empty, offset);
11311153
11321154 // we inserted the block in front of the condition
11331155 // so now check if condition matches. If not, break outside this block
......@@ -1135,11 +1157,11 @@ pub const Context = struct {
11351157 try writer.writeByte(wasm.opcode(.br_if));
11361158 try leb.writeULEB128(writer, @as(u32, 0));
11371159
1138 try self.genBody(condbr.else_body);
1160 try self.genBody(else_body);
11391161 try self.endBlock();
11401162
11411163 // Outer block that matches the condition
1142 try self.genBody(condbr.then_body);
1164 try self.genBody(then_body);
11431165
11441166 return .none;
11451167 }
......@@ -1149,21 +1171,23 @@ pub const Context = struct {
11491171 // the comparison that we can later jump back to
11501172 const offset = self.code.items.len;
11511173
1152 const lhs = self.resolveInst(inst.lhs);
1153 const rhs = self.resolveInst(inst.rhs);
1174 const data: Air.Inst.Data = self.air.instructions.items(.data)[inst];
1175 const lhs = self.resolveInst(data.bin_op.lhs);
1176 const rhs = self.resolveInst(data.bin_op.rhs);
1177 const lhs_ty = self.air.getRefType(data.bin_op.lhs);
11541178
11551179 try self.emitWValue(lhs);
11561180 try self.emitWValue(rhs);
11571181
11581182 const signedness: std.builtin.Signedness = blk: {
11591183 // by default we tell the operand type is unsigned (i.e. bools and enum values)
1160 if (inst.lhs.ty.zigTypeTag() != .Int) break :blk .unsigned;
1184 if (lhs_ty.zigTypeTag() != .Int) break :blk .unsigned;
11611185
11621186 // incase of an actual integer, we emit the correct signedness
1163 break :blk inst.lhs.ty.intInfo(self.target).signedness;
1187 break :blk lhs_ty.intInfo(self.target).signedness;
11641188 };
11651189 const opcode: wasm.Opcode = buildOpcode(.{
1166 .valtype1 = try self.typeToValtype(inst.lhs.ty),
1190 .valtype1 = try self.typeToValtype(lhs_ty),
11671191 .op = switch (op) {
11681192 .lt => .lt,
11691193 .lte => .le,
......@@ -1178,16 +1202,17 @@ pub const Context = struct {
11781202 return WValue{ .code_offset = offset };
11791203 }
11801204
1181 fn genBr(self: *Context, br: Air.Inst.Index) InnerError!WValue {
1205 fn genBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1206 const br = self.air.instructions.items(.data)[inst].br;
1207
11821208 // if operand has codegen bits we should break with a value
1183 if (br.operand.ty.hasCodeGenBits()) {
1184 const operand = self.resolveInst(br.operand);
1185 try self.emitWValue(operand);
1209 if (self.air.getRefType(br.operand).hasCodeGenBits()) {
1210 try self.emitWValue(self.resolveInst(br.operand));
11861211 }
11871212
11881213 // We map every block to its block index.
11891214 // We then determine how far we have to jump to it by substracting it from current block depth
1190 const idx: u32 = self.block_depth - self.blocks.get(br.block).?;
1215 const idx: u32 = self.block_depth - self.blocks.get(br.block_inst).?;
11911216 const writer = self.code.writer();
11921217 try writer.writeByte(wasm.opcode(.br));
11931218 try leb.writeULEB128(writer, idx);
......@@ -1195,10 +1220,11 @@ pub const Context = struct {
11951220 return .none;
11961221 }
11971222
1198 fn genNot(self: *Context, not: Air.Inst.Index) InnerError!WValue {
1223 fn genNot(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1224 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
11991225 const offset = self.code.items.len;
12001226
1201 const operand = self.resolveInst(not.operand);
1227 const operand = self.resolveInst(ty_op.operand);
12021228 try self.emitWValue(operand);
12031229
12041230 // wasm does not have booleans nor the `not` instruction, therefore compare with 0
......@@ -1212,35 +1238,44 @@ pub const Context = struct {
12121238 return WValue{ .code_offset = offset };
12131239 }
12141240
1215 fn genBreakpoint(self: *Context, breakpoint: Air.Inst.Index) InnerError!WValue {
1241 fn genBreakpoint(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
12161242 _ = self;
1217 _ = breakpoint;
1243 _ = inst;
12181244 // unsupported by wasm itself. Can be implemented once we support DWARF
12191245 // for wasm
12201246 return .none;
12211247 }
12221248
1223 fn genUnreachable(self: *Context, unreach: Air.Inst.Index) InnerError!WValue {
1224 _ = unreach;
1249 fn genUnreachable(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1250 _ = inst;
12251251 try self.code.append(wasm.opcode(.@"unreachable"));
12261252 return .none;
12271253 }
12281254
1229 fn genBitcast(self: *Context, bitcast: Air.Inst.Index) InnerError!WValue {
1230 return self.resolveInst(bitcast.operand);
1255 fn genBitcast(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1256 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1257 return self.resolveInst(ty_op.operand);
12311258 }
12321259
12331260 fn genStructFieldPtr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1234 const struct_ptr = self.resolveInst(inst.struct_ptr);
1261 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1262 const extra = self.air.extraData(Air.StructField, ty_pl.payload);
1263 const struct_ptr = self.resolveInst(extra.data.struct_ptr);
12351264
1236 return WValue{ .local = struct_ptr.multi_value.index + @intCast(u32, inst.field_index) };
1265 return WValue{ .local = struct_ptr.multi_value.index + @intCast(u32, extra.data.field_index) };
12371266 }
12381267
12391268 fn genSwitchBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1240 const target = self.resolveInst(inst.target);
1241 const target_ty = inst.target.ty;
1242 const valtype = try self.typeToValtype(.{ .node_offset = 0 }, target_ty);
1243 const blocktype = try self.genBlockType(inst.base.ty);
1269 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
1270 const extra = self.air.extraData(Air.SwitchBr, pl_op.payload);
1271 const cases = self.air.extra[extra.end..][0..extra.data.cases_len];
1272 const else_body = self.air.extra[extra.end + cases.len ..][0..extra.data.else_body_len];
1273
1274 const target = self.resolveInst(pl_op.operand);
1275 const target_ty = self.air.getRefType(pl_op.operand);
1276 const valtype = try self.typeToValtype(target_ty);
1277 // result type is always 'noreturn'
1278 const blocktype = wasm.block_empty;
12441279
12451280 const signedness: std.builtin.Signedness = blk: {
12461281 // by default we tell the operand type is unsigned (i.e. bools and enum values)
......@@ -1249,11 +1284,18 @@ pub const Context = struct {
12491284 // incase of an actual integer, we emit the correct signedness
12501285 break :blk target_ty.intInfo(self.target).signedness;
12511286 };
1252 for (inst.cases) |case| {
1287 for (cases) |case_idx| {
1288 const case = self.air.extraData(Air.SwitchBr.Case, case_idx);
1289 const case_body = self.air.extra[case.end..][0..case.data.body_len];
1290
12531291 // create a block for each case, when the condition does not match we break out of it
12541292 try self.startBlock(.block, blocktype, null);
12551293 try self.emitWValue(target);
1256 try self.emitConstant(.{ .node_offset = 0 }, case.item, target_ty);
1294
1295 // cases must represent a constant of which its type is in the `typed_value_map`
1296 // Therefore we can simply retrieve it.
1297 const ty_val = Air.Inst.Ref.typed_value_map[@enumToInt(case.data.item)];
1298 try self.emitConstant(ty_val.val, target_ty);
12571299 const opcode = buildOpcode(.{
12581300 .valtype1 = valtype,
12591301 .op = .ne, // not equal because we jump out the block if it does not match the condition
......@@ -1264,7 +1306,7 @@ pub const Context = struct {
12641306 try leb.writeULEB128(self.code.writer(), @as(u32, 0));
12651307
12661308 // emit our block code
1267 try self.genBody(case.body);
1309 try self.genBody(case_body);
12681310
12691311 // end the block we created earlier
12701312 try self.endBlock();
......@@ -1272,13 +1314,14 @@ pub const Context = struct {
12721314
12731315 // finally, emit the else case if it exists. Here we will not have to
12741316 // check for a condition, so also no need to emit a block.
1275 try self.genBody(inst.else_body);
1317 try self.genBody(else_body);
12761318
12771319 return .none;
12781320 }
12791321
12801322 fn genIsErr(self: *Context, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue {
1281 const operand = self.resolveInst(inst.operand);
1323 const un_op = self.air.instructions.items(.data)[inst].un_op;
1324 const operand = self.resolveInst(un_op);
12821325 const offset = self.code.items.len;
12831326 const writer = self.code.writer();
12841327
......@@ -1294,7 +1337,8 @@ pub const Context = struct {
12941337 }
12951338
12961339 fn genUnwrapErrUnionPayload(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1297 const operand = self.resolveInst(inst.operand);
1340 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1341 const operand = self.resolveInst(ty_op.operand);
12981342 // The index of multi_value contains the error code. To get the initial index of the payload we get
12991343 // the following index. Next, convert it to a `WValue.local`
13001344 //
......@@ -1303,6 +1347,7 @@ pub const Context = struct {
13031347 }
13041348
13051349 fn genWrapErrUnionPayload(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
1306 return self.resolveInst(inst.operand);
1350 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1351 return self.resolveInst(ty_op.operand);
13071352 }
13081353};
src/link/Wasm.zig+1-1
......@@ -220,7 +220,7 @@ pub fn updateFunc(self: *Wasm, module: *Module, func: *Module.Fn, air: Air, live
220220 defer context.deinit();
221221
222222 // generate the 'code' section for the function declaration
223 const result = context.genFunc(func) catch |err| switch (err) {
223 const result = context.genFunc() catch |err| switch (err) {
224224 error.CodegenFail => {
225225 decl.analysis = .codegen_failure;
226226 try module.failed_decls.put(module.gpa, decl, context.err_msg);