authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-16 21:57:46-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-16 21:57:46-07:00
logb58d8aa05f01e53003be32f220548cba69cf25dd
tree55f6bbc18b10fc19ccc290935899576e7c4c4dd7
parent091a98f524b41cac1fb299cdfdf911ae3b31c6ae

stage2: improve LLVM backend for enums

* support lowering enum types and constants to LLVM IR * fix cmp instruction to support enum operands

3 files changed, 59 insertions(+), 32 deletions(-)

src/codegen/llvm.zig+47-16
...@@ -588,6 +588,12 @@ pub const DeclGen = struct {...@@ -588,6 +588,12 @@ pub const DeclGen = struct {
588 const info = t.intInfo(self.module.getTarget());588 const info = t.intInfo(self.module.getTarget());
589 return self.context.intType(info.bits);589 return self.context.intType(info.bits);
590 },590 },
591 .Enum => {
592 var buffer: Type.Payload.Bits = undefined;
593 const int_ty = t.enumTagType(&buffer);
594 const bit_count = int_ty.intInfo(self.module.getTarget()).bits;
595 return self.context.intType(bit_count);
596 },
591 .Float => switch (t.floatBits(self.module.getTarget())) {597 .Float => switch (t.floatBits(self.module.getTarget())) {
592 16 => return self.context.halfType(),598 16 => return self.context.halfType(),
593 32 => return self.context.floatType(),599 32 => return self.context.floatType(),
...@@ -686,7 +692,6 @@ pub const DeclGen = struct {...@@ -686,7 +692,6 @@ pub const DeclGen = struct {
686692
687 .BoundFn => @panic("TODO remove BoundFn from the language"),693 .BoundFn => @panic("TODO remove BoundFn from the language"),
688694
689 .Enum,
690 .Union,695 .Union,
691 .Opaque,696 .Opaque,
692 .Frame,697 .Frame,
...@@ -723,6 +728,17 @@ pub const DeclGen = struct {...@@ -723,6 +728,17 @@ pub const DeclGen = struct {
723 }728 }
724 return llvm_int;729 return llvm_int;
725 },730 },
731 .Enum => {
732 const llvm_type = try self.llvmType(tv.ty);
733 const uint: u64 = uint: {
734 if (tv.val.castTag(.enum_field_index)) |payload| {
735 break :uint payload.data;
736 }
737 break :uint tv.val.toUnsignedInt();
738 };
739 const llvm_int = llvm_type.constInt(uint, .False);
740 return llvm_int;
741 },
726 .Float => {742 .Float => {
727 if (tv.ty.floatBits(self.module.getTarget()) <= 64) {743 if (tv.ty.floatBits(self.module.getTarget()) <= 64) {
728 const llvm_ty = try self.llvmType(tv.ty);744 const llvm_ty = try self.llvmType(tv.ty);
...@@ -907,7 +923,18 @@ pub const DeclGen = struct {...@@ -907,7 +923,18 @@ pub const DeclGen = struct {
907 .ComptimeFloat => unreachable,923 .ComptimeFloat => unreachable,
908 .Type => unreachable,924 .Type => unreachable,
909 .EnumLiteral => unreachable,925 .EnumLiteral => unreachable,
910 else => return self.todo("implement const of type '{}'", .{tv.ty}),926 .Void => unreachable,
927 .NoReturn => unreachable,
928 .Undefined => unreachable,
929 .Null => unreachable,
930 .BoundFn => unreachable,
931 .Opaque => unreachable,
932
933 .Union,
934 .Frame,
935 .AnyFrame,
936 .Vector,
937 => return self.todo("implement const of type '{}'", .{tv.ty}),
911 }938 }
912 }939 }
913940
...@@ -1195,21 +1222,15 @@ pub const FuncGen = struct {...@@ -1195,21 +1222,15 @@ pub const FuncGen = struct {
1195 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1222 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1196 const lhs = try self.resolveInst(bin_op.lhs);1223 const lhs = try self.resolveInst(bin_op.lhs);
1197 const rhs = try self.resolveInst(bin_op.rhs);1224 const rhs = try self.resolveInst(bin_op.rhs);
1198 const inst_ty = self.air.typeOfIndex(inst);1225 const operand_ty = self.air.typeOf(bin_op.lhs);
11991226
1200 switch (self.air.typeOf(bin_op.lhs).zigTypeTag()) {1227 const int_ty = switch (operand_ty.zigTypeTag()) {
1201 .Int, .Bool, .Pointer, .ErrorSet => {1228 .Enum => blk: {
1202 const is_signed = inst_ty.isSignedInt();1229 var buffer: Type.Payload.Bits = undefined;
1203 const operation = switch (op) {1230 const int_ty = operand_ty.enumTagType(&buffer);
1204 .eq => .EQ,1231 break :blk int_ty;
1205 .neq => .NE,
1206 .lt => @as(llvm.IntPredicate, if (is_signed) .SLT else .ULT),
1207 .lte => @as(llvm.IntPredicate, if (is_signed) .SLE else .ULE),
1208 .gt => @as(llvm.IntPredicate, if (is_signed) .SGT else .UGT),
1209 .gte => @as(llvm.IntPredicate, if (is_signed) .SGE else .UGE),
1210 };
1211 return self.builder.buildICmp(operation, lhs, rhs, "");
1212 },1232 },
1233 .Int, .Bool, .Pointer, .ErrorSet => operand_ty,
1213 .Float => {1234 .Float => {
1214 const operation: llvm.RealPredicate = switch (op) {1235 const operation: llvm.RealPredicate = switch (op) {
1215 .eq => .OEQ,1236 .eq => .OEQ,
...@@ -1222,7 +1243,17 @@ pub const FuncGen = struct {...@@ -1222,7 +1243,17 @@ pub const FuncGen = struct {
1222 return self.builder.buildFCmp(operation, lhs, rhs, "");1243 return self.builder.buildFCmp(operation, lhs, rhs, "");
1223 },1244 },
1224 else => unreachable,1245 else => unreachable,
1225 }1246 };
1247 const is_signed = int_ty.isSignedInt();
1248 const operation = switch (op) {
1249 .eq => .EQ,
1250 .neq => .NE,
1251 .lt => @as(llvm.IntPredicate, if (is_signed) .SLT else .ULT),
1252 .lte => @as(llvm.IntPredicate, if (is_signed) .SLE else .ULE),
1253 .gt => @as(llvm.IntPredicate, if (is_signed) .SGT else .UGT),
1254 .gte => @as(llvm.IntPredicate, if (is_signed) .SGE else .UGE),
1255 };
1256 return self.builder.buildICmp(operation, lhs, rhs, "");
1226 }1257 }
12271258
1228 fn airBlock(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {1259 fn airBlock(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
test/behavior/atomics.zig+12
...@@ -118,3 +118,15 @@ test "cmpxchg on a global variable" {...@@ -118,3 +118,15 @@ test "cmpxchg on a global variable" {
118 _ = @cmpxchgWeak(u32, &a_global_variable, 1234, 42, .Acquire, .Monotonic);118 _ = @cmpxchgWeak(u32, &a_global_variable, 1234, 42, .Acquire, .Monotonic);
119 try expect(a_global_variable == 42);119 try expect(a_global_variable == 42);
120}120}
121
122test "atomic load and rmw with enum" {
123 const Value = enum(u8) { a, b, c };
124 var x = Value.a;
125
126 try expect(@atomicLoad(Value, &x, .SeqCst) != .b);
127
128 _ = @atomicRmw(Value, &x, .Xchg, .c, .SeqCst);
129 try expect(@atomicLoad(Value, &x, .SeqCst) == .c);
130 try expect(@atomicLoad(Value, &x, .SeqCst) != .a);
131 try expect(@atomicLoad(Value, &x, .SeqCst) != .b);
132}
test/behavior/atomics_stage1.zig-16
...@@ -3,22 +3,6 @@ const expect = std.testing.expect;...@@ -3,22 +3,6 @@ const expect = std.testing.expect;
3const expectEqual = std.testing.expectEqual;3const expectEqual = std.testing.expectEqual;
4const builtin = @import("builtin");4const builtin = @import("builtin");
55
6test "atomic load and rmw with enum" {
7 const Value = enum(u8) {
8 a,
9 b,
10 c,
11 };
12 var x = Value.a;
13
14 try expect(@atomicLoad(Value, &x, .SeqCst) != .b);
15
16 _ = @atomicRmw(Value, &x, .Xchg, .c, .SeqCst);
17 try expect(@atomicLoad(Value, &x, .SeqCst) == .c);
18 try expect(@atomicLoad(Value, &x, .SeqCst) != .a);
19 try expect(@atomicLoad(Value, &x, .SeqCst) != .b);
20}
21
22test "atomic store" {6test "atomic store" {
23 var x: u32 = 0;7 var x: u32 = 0;
24 @atomicStore(u32, &x, 1, .SeqCst);8 @atomicStore(u32, &x, 1, .SeqCst);