From b58d8aa05f01e53003be32f220548cba69cf25dd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 16 Sep 2021 21:57:46 -0700 Subject: [PATCH] stage2: improve LLVM backend for enums * support lowering enum types and constants to LLVM IR * fix cmp instruction to support enum operands --- src/codegen/llvm.zig | 63 ++++++++++++++++++++++++-------- test/behavior/atomics.zig | 12 ++++++ test/behavior/atomics_stage1.zig | 16 -------- 3 files changed, 59 insertions(+), 32 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index ecc1790e6d..53e57ee219 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -588,6 +588,12 @@ pub const DeclGen = struct { const info = t.intInfo(self.module.getTarget()); return self.context.intType(info.bits); }, + .Enum => { + var buffer: Type.Payload.Bits = undefined; + const int_ty = t.enumTagType(&buffer); + const bit_count = int_ty.intInfo(self.module.getTarget()).bits; + return self.context.intType(bit_count); + }, .Float => switch (t.floatBits(self.module.getTarget())) { 16 => return self.context.halfType(), 32 => return self.context.floatType(), @@ -686,7 +692,6 @@ pub const DeclGen = struct { .BoundFn => @panic("TODO remove BoundFn from the language"), - .Enum, .Union, .Opaque, .Frame, @@ -723,6 +728,17 @@ pub const DeclGen = struct { } return llvm_int; }, + .Enum => { + const llvm_type = try self.llvmType(tv.ty); + const uint: u64 = uint: { + if (tv.val.castTag(.enum_field_index)) |payload| { + break :uint payload.data; + } + break :uint tv.val.toUnsignedInt(); + }; + const llvm_int = llvm_type.constInt(uint, .False); + return llvm_int; + }, .Float => { if (tv.ty.floatBits(self.module.getTarget()) <= 64) { const llvm_ty = try self.llvmType(tv.ty); @@ -907,7 +923,18 @@ pub const DeclGen = struct { .ComptimeFloat => unreachable, .Type => unreachable, .EnumLiteral => unreachable, - else => return self.todo("implement const of type '{}'", .{tv.ty}), + .Void => unreachable, + .NoReturn => unreachable, + .Undefined => unreachable, + .Null => unreachable, + .BoundFn => unreachable, + .Opaque => unreachable, + + .Union, + .Frame, + .AnyFrame, + .Vector, + => return self.todo("implement const of type '{}'", .{tv.ty}), } } @@ -1195,21 +1222,15 @@ pub const FuncGen = struct { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); - const inst_ty = self.air.typeOfIndex(inst); + const operand_ty = self.air.typeOf(bin_op.lhs); - switch (self.air.typeOf(bin_op.lhs).zigTypeTag()) { - .Int, .Bool, .Pointer, .ErrorSet => { - const is_signed = inst_ty.isSignedInt(); - const operation = switch (op) { - .eq => .EQ, - .neq => .NE, - .lt => @as(llvm.IntPredicate, if (is_signed) .SLT else .ULT), - .lte => @as(llvm.IntPredicate, if (is_signed) .SLE else .ULE), - .gt => @as(llvm.IntPredicate, if (is_signed) .SGT else .UGT), - .gte => @as(llvm.IntPredicate, if (is_signed) .SGE else .UGE), - }; - return self.builder.buildICmp(operation, lhs, rhs, ""); + const int_ty = switch (operand_ty.zigTypeTag()) { + .Enum => blk: { + var buffer: Type.Payload.Bits = undefined; + const int_ty = operand_ty.enumTagType(&buffer); + break :blk int_ty; }, + .Int, .Bool, .Pointer, .ErrorSet => operand_ty, .Float => { const operation: llvm.RealPredicate = switch (op) { .eq => .OEQ, @@ -1222,7 +1243,17 @@ pub const FuncGen = struct { return self.builder.buildFCmp(operation, lhs, rhs, ""); }, else => unreachable, - } + }; + const is_signed = int_ty.isSignedInt(); + const operation = switch (op) { + .eq => .EQ, + .neq => .NE, + .lt => @as(llvm.IntPredicate, if (is_signed) .SLT else .ULT), + .lte => @as(llvm.IntPredicate, if (is_signed) .SLE else .ULE), + .gt => @as(llvm.IntPredicate, if (is_signed) .SGT else .UGT), + .gte => @as(llvm.IntPredicate, if (is_signed) .SGE else .UGE), + }; + return self.builder.buildICmp(operation, lhs, rhs, ""); } fn airBlock(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { diff --git a/test/behavior/atomics.zig b/test/behavior/atomics.zig index 4e48913fef..efd63f1ac5 100644 --- a/test/behavior/atomics.zig +++ b/test/behavior/atomics.zig @@ -118,3 +118,15 @@ test "cmpxchg on a global variable" { _ = @cmpxchgWeak(u32, &a_global_variable, 1234, 42, .Acquire, .Monotonic); try expect(a_global_variable == 42); } + +test "atomic load and rmw with enum" { + const Value = enum(u8) { a, b, c }; + var x = Value.a; + + try expect(@atomicLoad(Value, &x, .SeqCst) != .b); + + _ = @atomicRmw(Value, &x, .Xchg, .c, .SeqCst); + try expect(@atomicLoad(Value, &x, .SeqCst) == .c); + try expect(@atomicLoad(Value, &x, .SeqCst) != .a); + try expect(@atomicLoad(Value, &x, .SeqCst) != .b); +} diff --git a/test/behavior/atomics_stage1.zig b/test/behavior/atomics_stage1.zig index 86082825c7..936c06b155 100644 --- a/test/behavior/atomics_stage1.zig +++ b/test/behavior/atomics_stage1.zig @@ -3,22 +3,6 @@ const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; const builtin = @import("builtin"); -test "atomic load and rmw with enum" { - const Value = enum(u8) { - a, - b, - c, - }; - var x = Value.a; - - try expect(@atomicLoad(Value, &x, .SeqCst) != .b); - - _ = @atomicRmw(Value, &x, .Xchg, .c, .SeqCst); - try expect(@atomicLoad(Value, &x, .SeqCst) == .c); - try expect(@atomicLoad(Value, &x, .SeqCst) != .a); - try expect(@atomicLoad(Value, &x, .SeqCst) != .b); -} - test "atomic store" { var x: u32 = 0; @atomicStore(u32, &x, 1, .SeqCst);