authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-05-20 14:03:54-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-05-20 14:03:54-04:00
logdf56bf94a2a3d1ddef891a90a5b1ee077b5dd6c9
tree169f07aa58fd905264dea3a20770ea24085932e2
parentb09936d72822e8bb7732f68c6ead369b83c39742
parent141a0cbb5a1ffd9e6c47bf859064139143ed8c51
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #8789 from Luukdegram/wasm-enum

stage2: wasm backend - Enums

2 files changed, 105 insertions(+), 25 deletions(-)

src/codegen/wasm.zig+70-25
...@@ -557,7 +557,14 @@ pub const Context = struct {...@@ -557,7 +557,14 @@ pub const Context = struct {
557 return self.fail(src, "Integer bit size not supported by wasm: '{d}'", .{info.bits});557 return self.fail(src, "Integer bit size not supported by wasm: '{d}'", .{info.bits});
558 },558 },
559 .Bool, .Pointer => wasm.Valtype.i32,559 .Bool, .Pointer => wasm.Valtype.i32,
560 else => self.fail(src, "TODO - Wasm valtype for type '{s}'", .{ty.tag()}),560 .Enum => switch (ty.tag()) {
561 .enum_simple => wasm.Valtype.i32,
562 else => self.typeToValtype(
563 src,
564 ty.cast(Type.Payload.EnumFull).?.data.tag_ty,
565 ),
566 },
567 else => self.fail(src, "TODO - Wasm valtype for type '{s}'", .{ty.zigTypeTag()}),
561 };568 };
562 }569 }
563570
...@@ -586,7 +593,7 @@ pub const Context = struct {...@@ -586,7 +593,7 @@ pub const Context = struct {
586 try writer.writeByte(wasm.opcode(.local_get));593 try writer.writeByte(wasm.opcode(.local_get));
587 try leb.writeULEB128(writer, idx);594 try leb.writeULEB128(writer, idx);
588 },595 },
589 .constant => |inst| try self.emitConstant(inst.castTag(.constant).?), // creates a new constant onto the stack596 .constant => |inst| try self.emitConstant(inst.src, inst.value().?, inst.ty), // creates a new constant onto the stack
590 }597 }
591 }598 }
592599
...@@ -707,14 +714,15 @@ pub const Context = struct {...@@ -707,14 +714,15 @@ pub const Context = struct {
707 .add => self.genBinOp(inst.castTag(.add).?, .add),714 .add => self.genBinOp(inst.castTag(.add).?, .add),
708 .alloc => self.genAlloc(inst.castTag(.alloc).?),715 .alloc => self.genAlloc(inst.castTag(.alloc).?),
709 .arg => self.genArg(inst.castTag(.arg).?),716 .arg => self.genArg(inst.castTag(.arg).?),
717 .bitcast => self.genBitcast(inst.castTag(.bitcast).?),
718 .bit_and => self.genBinOp(inst.castTag(.bit_and).?, .@"and"),
719 .bit_or => self.genBinOp(inst.castTag(.bit_or).?, .@"or"),
710 .block => self.genBlock(inst.castTag(.block).?),720 .block => self.genBlock(inst.castTag(.block).?),
721 .bool_and => self.genBinOp(inst.castTag(.bool_and).?, .@"and"),
722 .bool_or => self.genBinOp(inst.castTag(.bool_or).?, .@"or"),
711 .breakpoint => self.genBreakpoint(inst.castTag(.breakpoint).?),723 .breakpoint => self.genBreakpoint(inst.castTag(.breakpoint).?),
712 .br => self.genBr(inst.castTag(.br).?),724 .br => self.genBr(inst.castTag(.br).?),
713 .call => self.genCall(inst.castTag(.call).?),725 .call => self.genCall(inst.castTag(.call).?),
714 .bit_or => self.genBinOp(inst.castTag(.bit_or).?, .@"or"),
715 .bit_and => self.genBinOp(inst.castTag(.bit_and).?, .@"and"),
716 .bool_or => self.genBinOp(inst.castTag(.bool_or).?, .@"or"),
717 .bool_and => self.genBinOp(inst.castTag(.bool_and).?, .@"and"),
718 .cmp_eq => self.genCmp(inst.castTag(.cmp_eq).?, .eq),726 .cmp_eq => self.genCmp(inst.castTag(.cmp_eq).?, .eq),
719 .cmp_gte => self.genCmp(inst.castTag(.cmp_gte).?, .gte),727 .cmp_gte => self.genCmp(inst.castTag(.cmp_gte).?, .gte),
720 .cmp_gt => self.genCmp(inst.castTag(.cmp_gt).?, .gt),728 .cmp_gt => self.genCmp(inst.castTag(.cmp_gt).?, .gt),
...@@ -724,18 +732,18 @@ pub const Context = struct {...@@ -724,18 +732,18 @@ pub const Context = struct {
724 .condbr => self.genCondBr(inst.castTag(.condbr).?),732 .condbr => self.genCondBr(inst.castTag(.condbr).?),
725 .constant => unreachable,733 .constant => unreachable,
726 .dbg_stmt => WValue.none,734 .dbg_stmt => WValue.none,
735 .div => self.genBinOp(inst.castTag(.div).?, .div),
727 .load => self.genLoad(inst.castTag(.load).?),736 .load => self.genLoad(inst.castTag(.load).?),
728 .loop => self.genLoop(inst.castTag(.loop).?),737 .loop => self.genLoop(inst.castTag(.loop).?),
729 .mul => self.genBinOp(inst.castTag(.mul).?, .mul),738 .mul => self.genBinOp(inst.castTag(.mul).?, .mul),
730 .div => self.genBinOp(inst.castTag(.div).?, .div),
731 .xor => self.genBinOp(inst.castTag(.xor).?, .xor),
732 .not => self.genNot(inst.castTag(.not).?),739 .not => self.genNot(inst.castTag(.not).?),
733 .ret => self.genRet(inst.castTag(.ret).?),740 .ret => self.genRet(inst.castTag(.ret).?),
734 .retvoid => WValue.none,741 .retvoid => WValue.none,
735 .store => self.genStore(inst.castTag(.store).?),742 .store => self.genStore(inst.castTag(.store).?),
736 .sub => self.genBinOp(inst.castTag(.sub).?, .sub),743 .sub => self.genBinOp(inst.castTag(.sub).?, .sub),
737 .unreach => self.genUnreachable(inst.castTag(.unreach).?),744 .unreach => self.genUnreachable(inst.castTag(.unreach).?),
738 else => self.fail(inst.src, "TODO: Implement wasm inst: {s}", .{inst.tag}),745 .xor => self.genBinOp(inst.castTag(.xor).?, .xor),
746 else => self.fail(.{ .node_offset = 0 }, "TODO: Implement wasm inst: {s}", .{inst.tag}),
739 };747 };
740 }748 }
741749
...@@ -750,6 +758,7 @@ pub const Context = struct {...@@ -750,6 +758,7 @@ pub const Context = struct {
750 // TODO: Implement tail calls758 // TODO: Implement tail calls
751 const operand = self.resolveInst(inst.operand);759 const operand = self.resolveInst(inst.operand);
752 try self.emitWValue(operand);760 try self.emitWValue(operand);
761 try self.code.append(wasm.opcode(.@"return"));
753 return .none;762 return .none;
754 }763 }
755764
...@@ -830,44 +839,44 @@ pub const Context = struct {...@@ -830,44 +839,44 @@ pub const Context = struct {
830 return .none;839 return .none;
831 }840 }
832841
833 fn emitConstant(self: *Context, inst: *Inst.Constant) InnerError!void {842 fn emitConstant(self: *Context, src: LazySrcLoc, value: Value, ty: Type) InnerError!void {
834 const writer = self.code.writer();843 const writer = self.code.writer();
835 switch (inst.base.ty.zigTypeTag()) {844 switch (ty.zigTypeTag()) {
836 .Int => {845 .Int => {
837 // write opcode846 // write opcode
838 const opcode: wasm.Opcode = buildOpcode(.{847 const opcode: wasm.Opcode = buildOpcode(.{
839 .op = .@"const",848 .op = .@"const",
840 .valtype1 = try self.typeToValtype(inst.base.src, inst.base.ty),849 .valtype1 = try self.typeToValtype(src, ty),
841 });850 });
842 try writer.writeByte(wasm.opcode(opcode));851 try writer.writeByte(wasm.opcode(opcode));
843 // write constant852 // write constant
844 switch (inst.base.ty.intInfo(self.target).signedness) {853 switch (ty.intInfo(self.target).signedness) {
845 .signed => try leb.writeILEB128(writer, inst.val.toSignedInt()),854 .signed => try leb.writeILEB128(writer, value.toSignedInt()),
846 .unsigned => try leb.writeILEB128(writer, inst.val.toUnsignedInt()),855 .unsigned => try leb.writeILEB128(writer, value.toUnsignedInt()),
847 }856 }
848 },857 },
849 .Bool => {858 .Bool => {
850 // write opcode859 // write opcode
851 try writer.writeByte(wasm.opcode(.i32_const));860 try writer.writeByte(wasm.opcode(.i32_const));
852 // write constant861 // write constant
853 try leb.writeILEB128(writer, inst.val.toSignedInt());862 try leb.writeILEB128(writer, value.toSignedInt());
854 },863 },
855 .Float => {864 .Float => {
856 // write opcode865 // write opcode
857 const opcode: wasm.Opcode = buildOpcode(.{866 const opcode: wasm.Opcode = buildOpcode(.{
858 .op = .@"const",867 .op = .@"const",
859 .valtype1 = try self.typeToValtype(inst.base.src, inst.base.ty),868 .valtype1 = try self.typeToValtype(src, ty),
860 });869 });
861 try writer.writeByte(wasm.opcode(opcode));870 try writer.writeByte(wasm.opcode(opcode));
862 // write constant871 // write constant
863 switch (inst.base.ty.floatBits(self.target)) {872 switch (ty.floatBits(self.target)) {
864 0...32 => try writer.writeIntLittle(u32, @bitCast(u32, inst.val.toFloat(f32))),873 0...32 => try writer.writeIntLittle(u32, @bitCast(u32, value.toFloat(f32))),
865 64 => try writer.writeIntLittle(u64, @bitCast(u64, inst.val.toFloat(f64))),874 64 => try writer.writeIntLittle(u64, @bitCast(u64, value.toFloat(f64))),
866 else => |bits| return self.fail(inst.base.src, "Wasm TODO: emitConstant for float with {d} bits", .{bits}),875 else => |bits| return self.fail(src, "Wasm TODO: emitConstant for float with {d} bits", .{bits}),
867 }876 }
868 },877 },
869 .Pointer => {878 .Pointer => {
870 if (inst.val.castTag(.decl_ref)) |payload| {879 if (value.castTag(.decl_ref)) |payload| {
871 const decl = payload.data;880 const decl = payload.data;
872881
873 // offset into the offset table within the 'data' section882 // offset into the offset table within the 'data' section
...@@ -880,10 +889,35 @@ pub const Context = struct {...@@ -880,10 +889,35 @@ pub const Context = struct {
880 try writer.writeByte(wasm.opcode(.i32_load));889 try writer.writeByte(wasm.opcode(.i32_load));
881 try leb.writeULEB128(writer, @as(u32, 0));890 try leb.writeULEB128(writer, @as(u32, 0));
882 try leb.writeULEB128(writer, @as(u32, 0));891 try leb.writeULEB128(writer, @as(u32, 0));
883 } else return self.fail(inst.base.src, "Wasm TODO: emitConstant for other const pointer tag {s}", .{inst.val.tag()});892 } else return self.fail(src, "Wasm TODO: emitConstant for other const pointer tag {s}", .{value.tag()});
884 },893 },
885 .Void => {},894 .Void => {},
886 else => |ty| return self.fail(inst.base.src, "Wasm TODO: emitConstant for zigTypeTag {s}", .{ty}),895 .Enum => {
896 if (value.castTag(.enum_field_index)) |field_index| {
897 switch (ty.tag()) {
898 .enum_simple => {
899 try writer.writeByte(wasm.opcode(.i32_const));
900 try leb.writeULEB128(writer, field_index.data);
901 },
902 .enum_full, .enum_nonexhaustive => {
903 const enum_full = ty.cast(Type.Payload.EnumFull).?.data;
904 if (enum_full.values.count() != 0) {
905 const tag_val = enum_full.values.entries.items[field_index.data].key;
906 try self.emitConstant(src, tag_val, enum_full.tag_ty);
907 } else {
908 try writer.writeByte(wasm.opcode(.i32_const));
909 try leb.writeULEB128(writer, field_index.data);
910 }
911 },
912 else => unreachable,
913 }
914 } else {
915 var int_tag_buffer: Type.Payload.Bits = undefined;
916 const int_tag_ty = ty.intTagType(&int_tag_buffer);
917 try self.emitConstant(src, value, int_tag_ty);
918 }
919 },
920 else => |zig_type| return self.fail(src, "Wasm TODO: emitConstant for zigTypeTag {s}", .{zig_type}),
887 }921 }
888 }922 }
889923
...@@ -984,6 +1018,13 @@ pub const Context = struct {...@@ -984,6 +1018,13 @@ pub const Context = struct {
984 try self.emitWValue(lhs);1018 try self.emitWValue(lhs);
985 try self.emitWValue(rhs);1019 try self.emitWValue(rhs);
9861020
1021 const signedness: std.builtin.Signedness = blk: {
1022 // by default we tell the operand type is unsigned (i.e. bools and enum values)
1023 if (inst.lhs.ty.zigTypeTag() != .Int) break :blk .unsigned;
1024
1025 // incase of an actual integer, we emit the correct signedness
1026 break :blk inst.lhs.ty.intInfo(self.target).signedness;
1027 };
987 const opcode: wasm.Opcode = buildOpcode(.{1028 const opcode: wasm.Opcode = buildOpcode(.{
988 .valtype1 = try self.typeToValtype(inst.base.src, inst.lhs.ty),1029 .valtype1 = try self.typeToValtype(inst.base.src, inst.lhs.ty),
989 .op = switch (op) {1030 .op = switch (op) {
...@@ -994,7 +1035,7 @@ pub const Context = struct {...@@ -994,7 +1035,7 @@ pub const Context = struct {
994 .gte => .ge,1035 .gte => .ge,
995 .gt => .gt,1036 .gt => .gt,
996 },1037 },
997 .signedness = inst.lhs.ty.intInfo(self.target).signedness,1038 .signedness = signedness,
998 });1039 });
999 try self.code.append(wasm.opcode(opcode));1040 try self.code.append(wasm.opcode(opcode));
1000 return WValue{ .code_offset = offset };1041 return WValue{ .code_offset = offset };
...@@ -1045,4 +1086,8 @@ pub const Context = struct {...@@ -1045,4 +1086,8 @@ pub const Context = struct {
1045 try self.code.append(wasm.opcode(.@"unreachable"));1086 try self.code.append(wasm.opcode(.@"unreachable"));
1046 return .none;1087 return .none;
1047 }1088 }
1089
1090 fn genBitcast(self: *Context, bitcast: *Inst.UnOp) InnerError!WValue {
1091 return self.resolveInst(bitcast.operand);
1092 }
1048};1093};
test/stage2/wasm.zig+35
...@@ -384,4 +384,39 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -384,4 +384,39 @@ pub fn addCases(ctx: *TestContext) !void {
384 \\}384 \\}
385 , "5\n");385 , "5\n");
386 }386 }
387
388 {
389 var case = ctx.exe("wasm enum values", wasi);
390
391 case.addCompareOutput(
392 \\const Number = enum { One, Two, Three };
393 \\
394 \\pub export fn _start() i32 {
395 \\ var number1 = Number.One;
396 \\ var number2: Number = .Two;
397 \\ const number3 = @intToEnum(Number, 2);
398 \\
399 \\ return @enumToInt(number3);
400 \\}
401 , "2\n");
402
403 case.addCompareOutput(
404 \\const Number = enum { One, Two, Three };
405 \\
406 \\pub export fn _start() i32 {
407 \\ var number1 = Number.One;
408 \\ var number2: Number = .Two;
409 \\ const number3 = @intToEnum(Number, 2);
410 \\ if (number1 == number2) return 1;
411 \\ if (number2 == number3) return 1;
412 \\ if (@enumToInt(number1) != 0) return 1;
413 \\ if (@enumToInt(number2) != 1) return 1;
414 \\ if (@enumToInt(number3) != 2) return 1;
415 \\ var x: Number = .Two;
416 \\ if (number2 != x) return 1;
417 \\
418 \\ return @enumToInt(number3);
419 \\}
420 , "2\n");
421 }
387}422}