From c8fb36b36cd8368e84752770edf720e6e91ed997 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 27 Dec 2021 01:14:50 -0700 Subject: [PATCH] stage2: LLVM backend: implement `@tagName` for enums Introduced a new AIR instruction: `tag_name`. Reasons to do this instead of lowering it in Sema to a switch, function call, array lookup, or if-else tower: * Sema is a bottleneck; do less work in Sema whenever possible. * If any optimization passes run, and the operand to becomes comptime-known, then it could change to have a comptime result value instead of lowering to a function or array or something which would then have to be garbage-collected. * Backends may want to choose to use a function and a switch branch, or they may want to use a different strategy. Codegen for `@tagName` is implemented for the LLVM backend but not any others yet. Introduced some new `Type` tags: * `const_slice_u8_sentinel_0` * `manyptr_const_u8_sentinel_0` The motivation for this was to make typeof() on the tag_name AIR instruction non-allocating. A bunch more enum tests are passing now. --- src/Air.zig | 7 + src/Liveness.zig | 1 + src/Sema.zig | 51 +++++++- src/arch/aarch64/CodeGen.zig | 239 ++++++++++++++++++---------------- src/arch/arm/CodeGen.zig | 239 ++++++++++++++++++---------------- src/arch/riscv64/CodeGen.zig | 239 ++++++++++++++++++---------------- src/arch/x86_64/CodeGen.zig | 239 ++++++++++++++++++---------------- src/codegen/c.zig | 19 +++ src/codegen/llvm.zig | 146 +++++++++++++++++++-- src/codegen/llvm/bindings.zig | 15 +++ src/print_air.zig | 1 + src/type.zig | 155 +++++++++++++++++----- src/value.zig | 10 ++ test/behavior.zig | 1 + test/behavior/enum.zig | 123 +++++++++++++++++ test/behavior/enum_llvm.zig | 49 +++++++ test/behavior/enum_stage1.zig | 151 --------------------- 17 files changed, 1031 insertions(+), 654 deletions(-) create mode 100644 test/behavior/enum_llvm.zig diff --git a/src/Air.zig b/src/Air.zig index 0d660ff6ae..08e11716cc 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -496,6 +496,11 @@ pub const Inst = struct { /// Uses the `pl_op` field with payload `AtomicRmw`. Operand is `ptr`. atomic_rmw, + /// Given an enum tag value, returns the tag name. The enum type may be non-exhaustive. + /// Result type is always `[:0]const u8`. + /// Uses the `un_op` field. + tag_name, + pub fn fromCmpOp(op: std.math.CompareOperator) Tag { return switch (op) { .lt => .cmp_lt, @@ -811,6 +816,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .bool_to_int => return Type.initTag(.u1), + .tag_name => return Type.initTag(.const_slice_u8_sentinel_0), + .call => { const callee_ty = air.typeOf(datas[inst].pl_op.operand); switch (callee_ty.zigTypeTag()) { diff --git a/src/Liveness.zig b/src/Liveness.zig index a9f7fffca4..f3c194123d 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -333,6 +333,7 @@ fn analyzeInst( .bool_to_int, .ret, .ret_load, + .tag_name, => { const operand = inst_datas[inst].un_op; return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none }); diff --git a/src/Sema.zig b/src/Sema.zig index 3592cbf93d..977f2e1cb5 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2804,8 +2804,11 @@ fn zirStr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins const tracy = trace(@src()); defer tracy.end(); - const zir_bytes = sema.code.instructions.items(.data)[inst].str.get(sema.code); + const bytes = sema.code.instructions.items(.data)[inst].str.get(sema.code); + return sema.addStrLit(block, bytes); +} +fn addStrLit(sema: *Sema, block: *Block, zir_bytes: []const u8) CompileError!Air.Inst.Ref { // `zir_bytes` references memory inside the ZIR module, which can get deallocated // after semantic analysis is complete, for example in the case of the initialization // expression of a variable declaration. We need the memory to be in the new @@ -10045,8 +10048,50 @@ fn zirUnaryMath(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].un_node; + const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const src = inst_data.src(); - return sema.fail(block, src, "TODO: Sema.zirTagName", .{}); + const operand = sema.resolveInst(inst_data.operand); + const operand_ty = sema.typeOf(operand); + + const enum_ty = switch (operand_ty.zigTypeTag()) { + .Enum => operand_ty, + .Union => operand_ty.unionTagType() orelse { + const decl = operand_ty.getOwnerDecl(); + const msg = msg: { + const msg = try sema.errMsg(block, src, "union '{s}' is untagged", .{ + decl.name, + }); + errdefer msg.destroy(sema.gpa); + try sema.mod.errNoteNonLazy(decl.srcLoc(), msg, "declared here", .{}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); + }, + else => return sema.fail(block, operand_src, "expected enum or union; found {}", .{ + operand_ty, + }), + }; + const enum_decl = enum_ty.getOwnerDecl(); + const casted_operand = try sema.coerce(block, enum_ty, operand, operand_src); + if (try sema.resolveDefinedValue(block, operand_src, casted_operand)) |val| { + const field_index = enum_ty.enumTagFieldIndex(val) orelse { + const msg = msg: { + const msg = try sema.errMsg(block, src, "no field with value {} in enum '{s}'", .{ + casted_operand, enum_decl.name, + }); + errdefer msg.destroy(sema.gpa); + try sema.mod.errNoteNonLazy(enum_decl.srcLoc(), msg, "declared here", .{}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); + }; + const field_name = enum_ty.enumFieldName(field_index); + return sema.addStrLit(block, field_name); + } + // In case the value is runtime-known, we have an AIR instruction for this instead + // of trying to lower it in Sema because an optimization pass may result in the operand + // being comptime-known, which would let us elide the `tag_name` AIR instruction. + return block.addUnOp(.tag_name, casted_operand); } fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -15339,6 +15384,7 @@ fn typeHasOnePossibleValue( .array_sentinel, .array_u8_sentinel_0, .const_slice_u8, + .const_slice_u8_sentinel_0, .const_slice, .mut_slice, .anyopaque, @@ -15356,6 +15402,7 @@ fn typeHasOnePossibleValue( .var_args_param, .manyptr_u8, .manyptr_const_u8, + .manyptr_const_u8_sentinel_0, .atomic_order, .atomic_rmw_op, .calling_convention, diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 0142e6abf8..1a7105da31 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -504,133 +504,134 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { switch (air_tags[inst]) { // zig fmt: off - .add, .ptr_add => try self.airAdd(inst), - .addwrap => try self.airAddWrap(inst), - .add_sat => try self.airAddSat(inst), - .sub, .ptr_sub => try self.airSub(inst), - .subwrap => try self.airSubWrap(inst), - .sub_sat => try self.airSubSat(inst), - .mul => try self.airMul(inst), - .mulwrap => try self.airMulWrap(inst), - .mul_sat => try self.airMulSat(inst), - .rem => try self.airRem(inst), - .mod => try self.airMod(inst), - .shl, .shl_exact => try self.airShl(inst), - .shl_sat => try self.airShlSat(inst), - .min => try self.airMin(inst), - .max => try self.airMax(inst), - .slice => try self.airSlice(inst), + .add, .ptr_add => try self.airAdd(inst), + .addwrap => try self.airAddWrap(inst), + .add_sat => try self.airAddSat(inst), + .sub, .ptr_sub => try self.airSub(inst), + .subwrap => try self.airSubWrap(inst), + .sub_sat => try self.airSubSat(inst), + .mul => try self.airMul(inst), + .mulwrap => try self.airMulWrap(inst), + .mul_sat => try self.airMulSat(inst), + .rem => try self.airRem(inst), + .mod => try self.airMod(inst), + .shl, .shl_exact => try self.airShl(inst), + .shl_sat => try self.airShlSat(inst), + .min => try self.airMin(inst), + .max => try self.airMax(inst), + .slice => try self.airSlice(inst), - .add_with_overflow => try self.airAddWithOverflow(inst), - .sub_with_overflow => try self.airSubWithOverflow(inst), - .mul_with_overflow => try self.airMulWithOverflow(inst), - .shl_with_overflow => try self.airShlWithOverflow(inst), + .add_with_overflow => try self.airAddWithOverflow(inst), + .sub_with_overflow => try self.airSubWithOverflow(inst), + .mul_with_overflow => try self.airMulWithOverflow(inst), + .shl_with_overflow => try self.airShlWithOverflow(inst), - .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), + .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), - .cmp_lt => try self.airCmp(inst, .lt), - .cmp_lte => try self.airCmp(inst, .lte), - .cmp_eq => try self.airCmp(inst, .eq), - .cmp_gte => try self.airCmp(inst, .gte), - .cmp_gt => try self.airCmp(inst, .gt), - .cmp_neq => try self.airCmp(inst, .neq), + .cmp_lt => try self.airCmp(inst, .lt), + .cmp_lte => try self.airCmp(inst, .lte), + .cmp_eq => try self.airCmp(inst, .eq), + .cmp_gte => try self.airCmp(inst, .gte), + .cmp_gt => try self.airCmp(inst, .gt), + .cmp_neq => try self.airCmp(inst, .neq), - .bool_and => try self.airBoolOp(inst), - .bool_or => try self.airBoolOp(inst), - .bit_and => try self.airBitAnd(inst), - .bit_or => try self.airBitOr(inst), - .xor => try self.airXor(inst), - .shr => try self.airShr(inst), + .bool_and => try self.airBoolOp(inst), + .bool_or => try self.airBoolOp(inst), + .bit_and => try self.airBitAnd(inst), + .bit_or => try self.airBitOr(inst), + .xor => try self.airXor(inst), + .shr => try self.airShr(inst), - .alloc => try self.airAlloc(inst), - .ret_ptr => try self.airRetPtr(inst), - .arg => try self.airArg(inst), - .assembly => try self.airAsm(inst), - .bitcast => try self.airBitCast(inst), - .block => try self.airBlock(inst), - .br => try self.airBr(inst), - .breakpoint => try self.airBreakpoint(), - .ret_addr => try self.airRetAddr(), - .fence => try self.airFence(), - .call => try self.airCall(inst), - .cond_br => try self.airCondBr(inst), - .dbg_stmt => try self.airDbgStmt(inst), - .fptrunc => try self.airFptrunc(inst), - .fpext => try self.airFpext(inst), - .intcast => try self.airIntCast(inst), - .trunc => try self.airTrunc(inst), - .bool_to_int => try self.airBoolToInt(inst), - .is_non_null => try self.airIsNonNull(inst), - .is_non_null_ptr => try self.airIsNonNullPtr(inst), - .is_null => try self.airIsNull(inst), - .is_null_ptr => try self.airIsNullPtr(inst), - .is_non_err => try self.airIsNonErr(inst), - .is_non_err_ptr => try self.airIsNonErrPtr(inst), - .is_err => try self.airIsErr(inst), - .is_err_ptr => try self.airIsErrPtr(inst), - .load => try self.airLoad(inst), - .loop => try self.airLoop(inst), - .not => try self.airNot(inst), - .ptrtoint => try self.airPtrToInt(inst), - .ret => try self.airRet(inst), - .ret_load => try self.airRetLoad(inst), - .store => try self.airStore(inst), - .struct_field_ptr=> try self.airStructFieldPtr(inst), - .struct_field_val=> try self.airStructFieldVal(inst), - .array_to_slice => try self.airArrayToSlice(inst), - .int_to_float => try self.airIntToFloat(inst), - .float_to_int => try self.airFloatToInt(inst), - .cmpxchg_strong => try self.airCmpxchg(inst), - .cmpxchg_weak => try self.airCmpxchg(inst), - .atomic_rmw => try self.airAtomicRmw(inst), - .atomic_load => try self.airAtomicLoad(inst), - .memcpy => try self.airMemcpy(inst), - .memset => try self.airMemset(inst), - .set_union_tag => try self.airSetUnionTag(inst), - .get_union_tag => try self.airGetUnionTag(inst), - .clz => try self.airClz(inst), - .ctz => try self.airCtz(inst), - .popcount => try self.airPopcount(inst), + .alloc => try self.airAlloc(inst), + .ret_ptr => try self.airRetPtr(inst), + .arg => try self.airArg(inst), + .assembly => try self.airAsm(inst), + .bitcast => try self.airBitCast(inst), + .block => try self.airBlock(inst), + .br => try self.airBr(inst), + .breakpoint => try self.airBreakpoint(), + .ret_addr => try self.airRetAddr(), + .fence => try self.airFence(), + .call => try self.airCall(inst), + .cond_br => try self.airCondBr(inst), + .dbg_stmt => try self.airDbgStmt(inst), + .fptrunc => try self.airFptrunc(inst), + .fpext => try self.airFpext(inst), + .intcast => try self.airIntCast(inst), + .trunc => try self.airTrunc(inst), + .bool_to_int => try self.airBoolToInt(inst), + .is_non_null => try self.airIsNonNull(inst), + .is_non_null_ptr => try self.airIsNonNullPtr(inst), + .is_null => try self.airIsNull(inst), + .is_null_ptr => try self.airIsNullPtr(inst), + .is_non_err => try self.airIsNonErr(inst), + .is_non_err_ptr => try self.airIsNonErrPtr(inst), + .is_err => try self.airIsErr(inst), + .is_err_ptr => try self.airIsErrPtr(inst), + .load => try self.airLoad(inst), + .loop => try self.airLoop(inst), + .not => try self.airNot(inst), + .ptrtoint => try self.airPtrToInt(inst), + .ret => try self.airRet(inst), + .ret_load => try self.airRetLoad(inst), + .store => try self.airStore(inst), + .struct_field_ptr=> try self.airStructFieldPtr(inst), + .struct_field_val=> try self.airStructFieldVal(inst), + .array_to_slice => try self.airArrayToSlice(inst), + .int_to_float => try self.airIntToFloat(inst), + .float_to_int => try self.airFloatToInt(inst), + .cmpxchg_strong => try self.airCmpxchg(inst), + .cmpxchg_weak => try self.airCmpxchg(inst), + .atomic_rmw => try self.airAtomicRmw(inst), + .atomic_load => try self.airAtomicLoad(inst), + .memcpy => try self.airMemcpy(inst), + .memset => try self.airMemset(inst), + .set_union_tag => try self.airSetUnionTag(inst), + .get_union_tag => try self.airGetUnionTag(inst), + .clz => try self.airClz(inst), + .ctz => try self.airCtz(inst), + .popcount => try self.airPopcount(inst), + .tag_name => try self.airTagName(inst), - .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), - .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), - .atomic_store_release => try self.airAtomicStore(inst, .Release), - .atomic_store_seq_cst => try self.airAtomicStore(inst, .SeqCst), + .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), + .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), + .atomic_store_release => try self.airAtomicStore(inst, .Release), + .atomic_store_seq_cst => try self.airAtomicStore(inst, .SeqCst), - .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0), - .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1), - .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2), - .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3), + .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0), + .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1), + .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2), + .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3), - .switch_br => try self.airSwitch(inst), - .slice_ptr => try self.airSlicePtr(inst), - .slice_len => try self.airSliceLen(inst), + .switch_br => try self.airSwitch(inst), + .slice_ptr => try self.airSlicePtr(inst), + .slice_len => try self.airSliceLen(inst), - .ptr_slice_len_ptr => try self.airPtrSliceLenPtr(inst), - .ptr_slice_ptr_ptr => try self.airPtrSlicePtrPtr(inst), + .ptr_slice_len_ptr => try self.airPtrSliceLenPtr(inst), + .ptr_slice_ptr_ptr => try self.airPtrSlicePtrPtr(inst), - .array_elem_val => try self.airArrayElemVal(inst), - .slice_elem_val => try self.airSliceElemVal(inst), - .slice_elem_ptr => try self.airSliceElemPtr(inst), - .ptr_elem_val => try self.airPtrElemVal(inst), - .ptr_elem_ptr => try self.airPtrElemPtr(inst), + .array_elem_val => try self.airArrayElemVal(inst), + .slice_elem_val => try self.airSliceElemVal(inst), + .slice_elem_ptr => try self.airSliceElemPtr(inst), + .ptr_elem_val => try self.airPtrElemVal(inst), + .ptr_elem_ptr => try self.airPtrElemPtr(inst), - .constant => unreachable, // excluded from function bodies - .const_ty => unreachable, // excluded from function bodies - .unreach => self.finishAirBookkeeping(), + .constant => unreachable, // excluded from function bodies + .const_ty => unreachable, // excluded from function bodies + .unreach => self.finishAirBookkeeping(), - .optional_payload => try self.airOptionalPayload(inst), - .optional_payload_ptr => try self.airOptionalPayloadPtr(inst), - .optional_payload_ptr_set => try self.airOptionalPayloadPtrSet(inst), - .unwrap_errunion_err => try self.airUnwrapErrErr(inst), - .unwrap_errunion_payload => try self.airUnwrapErrPayload(inst), - .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst), - .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst), + .optional_payload => try self.airOptionalPayload(inst), + .optional_payload_ptr => try self.airOptionalPayloadPtr(inst), + .optional_payload_ptr_set => try self.airOptionalPayloadPtrSet(inst), + .unwrap_errunion_err => try self.airUnwrapErrErr(inst), + .unwrap_errunion_payload => try self.airUnwrapErrPayload(inst), + .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst), + .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst), - .wrap_optional => try self.airWrapOptional(inst), - .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), - .wrap_errunion_err => try self.airWrapErrUnionErr(inst), - // zig fmt: on + .wrap_optional => try self.airWrapOptional(inst), + .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), + .wrap_errunion_err => try self.airWrapErrUnionErr(inst), + // zig fmt: on } if (std.debug.runtime_safety) { if (self.air_bookkeeping < old_air_bookkeeping + 1) { @@ -2546,6 +2547,16 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch}); } +fn airTagName(self: *Self, inst: Air.Inst.Index) !void { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const operand = try self.resolveInst(un_op); + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { + _ = operand; + return self.fail("TODO implement airTagName for aarch64", .{}); + }; + return self.finishAir(inst, result, .{ un_op, .none, .none }); +} + fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { // First section of indexes correspond to a set number of constant values. const ref_int = @enumToInt(inst); diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index ee68daaecf..2b7610e761 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -502,133 +502,134 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { switch (air_tags[inst]) { // zig fmt: off - .add, .ptr_add => try self.airAdd(inst), - .addwrap => try self.airAddWrap(inst), - .add_sat => try self.airAddSat(inst), - .sub, .ptr_sub => try self.airSub(inst), - .subwrap => try self.airSubWrap(inst), - .sub_sat => try self.airSubSat(inst), - .mul => try self.airMul(inst), - .mulwrap => try self.airMulWrap(inst), - .mul_sat => try self.airMulSat(inst), - .rem => try self.airRem(inst), - .mod => try self.airMod(inst), - .shl, .shl_exact => try self.airShl(inst), - .shl_sat => try self.airShlSat(inst), - .min => try self.airMin(inst), - .max => try self.airMax(inst), - .slice => try self.airSlice(inst), + .add, .ptr_add => try self.airAdd(inst), + .addwrap => try self.airAddWrap(inst), + .add_sat => try self.airAddSat(inst), + .sub, .ptr_sub => try self.airSub(inst), + .subwrap => try self.airSubWrap(inst), + .sub_sat => try self.airSubSat(inst), + .mul => try self.airMul(inst), + .mulwrap => try self.airMulWrap(inst), + .mul_sat => try self.airMulSat(inst), + .rem => try self.airRem(inst), + .mod => try self.airMod(inst), + .shl, .shl_exact => try self.airShl(inst), + .shl_sat => try self.airShlSat(inst), + .min => try self.airMin(inst), + .max => try self.airMax(inst), + .slice => try self.airSlice(inst), - .add_with_overflow => try self.airAddWithOverflow(inst), - .sub_with_overflow => try self.airSubWithOverflow(inst), - .mul_with_overflow => try self.airMulWithOverflow(inst), - .shl_with_overflow => try self.airShlWithOverflow(inst), + .add_with_overflow => try self.airAddWithOverflow(inst), + .sub_with_overflow => try self.airSubWithOverflow(inst), + .mul_with_overflow => try self.airMulWithOverflow(inst), + .shl_with_overflow => try self.airShlWithOverflow(inst), - .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), + .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), - .cmp_lt => try self.airCmp(inst, .lt), - .cmp_lte => try self.airCmp(inst, .lte), - .cmp_eq => try self.airCmp(inst, .eq), - .cmp_gte => try self.airCmp(inst, .gte), - .cmp_gt => try self.airCmp(inst, .gt), - .cmp_neq => try self.airCmp(inst, .neq), + .cmp_lt => try self.airCmp(inst, .lt), + .cmp_lte => try self.airCmp(inst, .lte), + .cmp_eq => try self.airCmp(inst, .eq), + .cmp_gte => try self.airCmp(inst, .gte), + .cmp_gt => try self.airCmp(inst, .gt), + .cmp_neq => try self.airCmp(inst, .neq), - .bool_and => try self.airBoolOp(inst), - .bool_or => try self.airBoolOp(inst), - .bit_and => try self.airBitAnd(inst), - .bit_or => try self.airBitOr(inst), - .xor => try self.airXor(inst), - .shr => try self.airShr(inst), + .bool_and => try self.airBoolOp(inst), + .bool_or => try self.airBoolOp(inst), + .bit_and => try self.airBitAnd(inst), + .bit_or => try self.airBitOr(inst), + .xor => try self.airXor(inst), + .shr => try self.airShr(inst), - .alloc => try self.airAlloc(inst), - .ret_ptr => try self.airRetPtr(inst), - .arg => try self.airArg(inst), - .assembly => try self.airAsm(inst), - .bitcast => try self.airBitCast(inst), - .block => try self.airBlock(inst), - .br => try self.airBr(inst), - .breakpoint => try self.airBreakpoint(), - .ret_addr => try self.airRetAddr(), - .fence => try self.airFence(), - .call => try self.airCall(inst), - .cond_br => try self.airCondBr(inst), - .dbg_stmt => try self.airDbgStmt(inst), - .fptrunc => try self.airFptrunc(inst), - .fpext => try self.airFpext(inst), - .intcast => try self.airIntCast(inst), - .trunc => try self.airTrunc(inst), - .bool_to_int => try self.airBoolToInt(inst), - .is_non_null => try self.airIsNonNull(inst), - .is_non_null_ptr => try self.airIsNonNullPtr(inst), - .is_null => try self.airIsNull(inst), - .is_null_ptr => try self.airIsNullPtr(inst), - .is_non_err => try self.airIsNonErr(inst), - .is_non_err_ptr => try self.airIsNonErrPtr(inst), - .is_err => try self.airIsErr(inst), - .is_err_ptr => try self.airIsErrPtr(inst), - .load => try self.airLoad(inst), - .loop => try self.airLoop(inst), - .not => try self.airNot(inst), - .ptrtoint => try self.airPtrToInt(inst), - .ret => try self.airRet(inst), - .ret_load => try self.airRetLoad(inst), - .store => try self.airStore(inst), - .struct_field_ptr=> try self.airStructFieldPtr(inst), - .struct_field_val=> try self.airStructFieldVal(inst), - .array_to_slice => try self.airArrayToSlice(inst), - .int_to_float => try self.airIntToFloat(inst), - .float_to_int => try self.airFloatToInt(inst), - .cmpxchg_strong => try self.airCmpxchg(inst), - .cmpxchg_weak => try self.airCmpxchg(inst), - .atomic_rmw => try self.airAtomicRmw(inst), - .atomic_load => try self.airAtomicLoad(inst), - .memcpy => try self.airMemcpy(inst), - .memset => try self.airMemset(inst), - .set_union_tag => try self.airSetUnionTag(inst), - .get_union_tag => try self.airGetUnionTag(inst), - .clz => try self.airClz(inst), - .ctz => try self.airCtz(inst), - .popcount => try self.airPopcount(inst), + .alloc => try self.airAlloc(inst), + .ret_ptr => try self.airRetPtr(inst), + .arg => try self.airArg(inst), + .assembly => try self.airAsm(inst), + .bitcast => try self.airBitCast(inst), + .block => try self.airBlock(inst), + .br => try self.airBr(inst), + .breakpoint => try self.airBreakpoint(), + .ret_addr => try self.airRetAddr(), + .fence => try self.airFence(), + .call => try self.airCall(inst), + .cond_br => try self.airCondBr(inst), + .dbg_stmt => try self.airDbgStmt(inst), + .fptrunc => try self.airFptrunc(inst), + .fpext => try self.airFpext(inst), + .intcast => try self.airIntCast(inst), + .trunc => try self.airTrunc(inst), + .bool_to_int => try self.airBoolToInt(inst), + .is_non_null => try self.airIsNonNull(inst), + .is_non_null_ptr => try self.airIsNonNullPtr(inst), + .is_null => try self.airIsNull(inst), + .is_null_ptr => try self.airIsNullPtr(inst), + .is_non_err => try self.airIsNonErr(inst), + .is_non_err_ptr => try self.airIsNonErrPtr(inst), + .is_err => try self.airIsErr(inst), + .is_err_ptr => try self.airIsErrPtr(inst), + .load => try self.airLoad(inst), + .loop => try self.airLoop(inst), + .not => try self.airNot(inst), + .ptrtoint => try self.airPtrToInt(inst), + .ret => try self.airRet(inst), + .ret_load => try self.airRetLoad(inst), + .store => try self.airStore(inst), + .struct_field_ptr=> try self.airStructFieldPtr(inst), + .struct_field_val=> try self.airStructFieldVal(inst), + .array_to_slice => try self.airArrayToSlice(inst), + .int_to_float => try self.airIntToFloat(inst), + .float_to_int => try self.airFloatToInt(inst), + .cmpxchg_strong => try self.airCmpxchg(inst), + .cmpxchg_weak => try self.airCmpxchg(inst), + .atomic_rmw => try self.airAtomicRmw(inst), + .atomic_load => try self.airAtomicLoad(inst), + .memcpy => try self.airMemcpy(inst), + .memset => try self.airMemset(inst), + .set_union_tag => try self.airSetUnionTag(inst), + .get_union_tag => try self.airGetUnionTag(inst), + .clz => try self.airClz(inst), + .ctz => try self.airCtz(inst), + .popcount => try self.airPopcount(inst), + .tag_name => try self.airTagName(inst), - .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), - .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), - .atomic_store_release => try self.airAtomicStore(inst, .Release), - .atomic_store_seq_cst => try self.airAtomicStore(inst, .SeqCst), + .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), + .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), + .atomic_store_release => try self.airAtomicStore(inst, .Release), + .atomic_store_seq_cst => try self.airAtomicStore(inst, .SeqCst), - .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0), - .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1), - .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2), - .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3), + .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0), + .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1), + .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2), + .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3), - .switch_br => try self.airSwitch(inst), - .slice_ptr => try self.airSlicePtr(inst), - .slice_len => try self.airSliceLen(inst), + .switch_br => try self.airSwitch(inst), + .slice_ptr => try self.airSlicePtr(inst), + .slice_len => try self.airSliceLen(inst), - .ptr_slice_len_ptr => try self.airPtrSliceLenPtr(inst), - .ptr_slice_ptr_ptr => try self.airPtrSlicePtrPtr(inst), + .ptr_slice_len_ptr => try self.airPtrSliceLenPtr(inst), + .ptr_slice_ptr_ptr => try self.airPtrSlicePtrPtr(inst), - .array_elem_val => try self.airArrayElemVal(inst), - .slice_elem_val => try self.airSliceElemVal(inst), - .slice_elem_ptr => try self.airSliceElemPtr(inst), - .ptr_elem_val => try self.airPtrElemVal(inst), - .ptr_elem_ptr => try self.airPtrElemPtr(inst), + .array_elem_val => try self.airArrayElemVal(inst), + .slice_elem_val => try self.airSliceElemVal(inst), + .slice_elem_ptr => try self.airSliceElemPtr(inst), + .ptr_elem_val => try self.airPtrElemVal(inst), + .ptr_elem_ptr => try self.airPtrElemPtr(inst), - .constant => unreachable, // excluded from function bodies - .const_ty => unreachable, // excluded from function bodies - .unreach => self.finishAirBookkeeping(), + .constant => unreachable, // excluded from function bodies + .const_ty => unreachable, // excluded from function bodies + .unreach => self.finishAirBookkeeping(), - .optional_payload => try self.airOptionalPayload(inst), - .optional_payload_ptr => try self.airOptionalPayloadPtr(inst), - .optional_payload_ptr_set => try self.airOptionalPayloadPtrSet(inst), - .unwrap_errunion_err => try self.airUnwrapErrErr(inst), - .unwrap_errunion_payload => try self.airUnwrapErrPayload(inst), - .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst), - .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst), + .optional_payload => try self.airOptionalPayload(inst), + .optional_payload_ptr => try self.airOptionalPayloadPtr(inst), + .optional_payload_ptr_set => try self.airOptionalPayloadPtrSet(inst), + .unwrap_errunion_err => try self.airUnwrapErrErr(inst), + .unwrap_errunion_payload => try self.airUnwrapErrPayload(inst), + .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst), + .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst), - .wrap_optional => try self.airWrapOptional(inst), - .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), - .wrap_errunion_err => try self.airWrapErrUnionErr(inst), - // zig fmt: on + .wrap_optional => try self.airWrapOptional(inst), + .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), + .wrap_errunion_err => try self.airWrapErrUnionErr(inst), + // zig fmt: on } if (std.debug.runtime_safety) { if (self.air_bookkeeping < old_air_bookkeeping + 1) { @@ -3301,6 +3302,16 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch}); } +fn airTagName(self: *Self, inst: Air.Inst.Index) !void { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const operand = try self.resolveInst(un_op); + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { + _ = operand; + return self.fail("TODO implement airTagName for arm", .{}); + }; + return self.finishAir(inst, result, .{ un_op, .none, .none }); +} + fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { // First section of indexes correspond to a set number of constant values. const ref_int = @enumToInt(inst); diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 3930e7364e..ddb87c4651 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -483,133 +483,134 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { switch (air_tags[inst]) { // zig fmt: off - .add, .ptr_add => try self.airAdd(inst), - .addwrap => try self.airAddWrap(inst), - .add_sat => try self.airAddSat(inst), - .sub, .ptr_sub => try self.airSub(inst), - .subwrap => try self.airSubWrap(inst), - .sub_sat => try self.airSubSat(inst), - .mul => try self.airMul(inst), - .mulwrap => try self.airMulWrap(inst), - .mul_sat => try self.airMulSat(inst), - .rem => try self.airRem(inst), - .mod => try self.airMod(inst), - .shl, .shl_exact => try self.airShl(inst), - .shl_sat => try self.airShlSat(inst), - .min => try self.airMin(inst), - .max => try self.airMax(inst), - .slice => try self.airSlice(inst), + .add, .ptr_add => try self.airAdd(inst), + .addwrap => try self.airAddWrap(inst), + .add_sat => try self.airAddSat(inst), + .sub, .ptr_sub => try self.airSub(inst), + .subwrap => try self.airSubWrap(inst), + .sub_sat => try self.airSubSat(inst), + .mul => try self.airMul(inst), + .mulwrap => try self.airMulWrap(inst), + .mul_sat => try self.airMulSat(inst), + .rem => try self.airRem(inst), + .mod => try self.airMod(inst), + .shl, .shl_exact => try self.airShl(inst), + .shl_sat => try self.airShlSat(inst), + .min => try self.airMin(inst), + .max => try self.airMax(inst), + .slice => try self.airSlice(inst), - .add_with_overflow => try self.airAddWithOverflow(inst), - .sub_with_overflow => try self.airSubWithOverflow(inst), - .mul_with_overflow => try self.airMulWithOverflow(inst), - .shl_with_overflow => try self.airShlWithOverflow(inst), + .add_with_overflow => try self.airAddWithOverflow(inst), + .sub_with_overflow => try self.airSubWithOverflow(inst), + .mul_with_overflow => try self.airMulWithOverflow(inst), + .shl_with_overflow => try self.airShlWithOverflow(inst), - .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), + .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), - .cmp_lt => try self.airCmp(inst, .lt), - .cmp_lte => try self.airCmp(inst, .lte), - .cmp_eq => try self.airCmp(inst, .eq), - .cmp_gte => try self.airCmp(inst, .gte), - .cmp_gt => try self.airCmp(inst, .gt), - .cmp_neq => try self.airCmp(inst, .neq), + .cmp_lt => try self.airCmp(inst, .lt), + .cmp_lte => try self.airCmp(inst, .lte), + .cmp_eq => try self.airCmp(inst, .eq), + .cmp_gte => try self.airCmp(inst, .gte), + .cmp_gt => try self.airCmp(inst, .gt), + .cmp_neq => try self.airCmp(inst, .neq), - .bool_and => try self.airBoolOp(inst), - .bool_or => try self.airBoolOp(inst), - .bit_and => try self.airBitAnd(inst), - .bit_or => try self.airBitOr(inst), - .xor => try self.airXor(inst), - .shr => try self.airShr(inst), + .bool_and => try self.airBoolOp(inst), + .bool_or => try self.airBoolOp(inst), + .bit_and => try self.airBitAnd(inst), + .bit_or => try self.airBitOr(inst), + .xor => try self.airXor(inst), + .shr => try self.airShr(inst), - .alloc => try self.airAlloc(inst), - .ret_ptr => try self.airRetPtr(inst), - .arg => try self.airArg(inst), - .assembly => try self.airAsm(inst), - .bitcast => try self.airBitCast(inst), - .block => try self.airBlock(inst), - .br => try self.airBr(inst), - .breakpoint => try self.airBreakpoint(), - .ret_addr => try self.airRetAddr(), - .fence => try self.airFence(), - .call => try self.airCall(inst), - .cond_br => try self.airCondBr(inst), - .dbg_stmt => try self.airDbgStmt(inst), - .fptrunc => try self.airFptrunc(inst), - .fpext => try self.airFpext(inst), - .intcast => try self.airIntCast(inst), - .trunc => try self.airTrunc(inst), - .bool_to_int => try self.airBoolToInt(inst), - .is_non_null => try self.airIsNonNull(inst), - .is_non_null_ptr => try self.airIsNonNullPtr(inst), - .is_null => try self.airIsNull(inst), - .is_null_ptr => try self.airIsNullPtr(inst), - .is_non_err => try self.airIsNonErr(inst), - .is_non_err_ptr => try self.airIsNonErrPtr(inst), - .is_err => try self.airIsErr(inst), - .is_err_ptr => try self.airIsErrPtr(inst), - .load => try self.airLoad(inst), - .loop => try self.airLoop(inst), - .not => try self.airNot(inst), - .ptrtoint => try self.airPtrToInt(inst), - .ret => try self.airRet(inst), - .ret_load => try self.airRetLoad(inst), - .store => try self.airStore(inst), - .struct_field_ptr=> try self.airStructFieldPtr(inst), - .struct_field_val=> try self.airStructFieldVal(inst), - .array_to_slice => try self.airArrayToSlice(inst), - .int_to_float => try self.airIntToFloat(inst), - .float_to_int => try self.airFloatToInt(inst), - .cmpxchg_strong => try self.airCmpxchg(inst), - .cmpxchg_weak => try self.airCmpxchg(inst), - .atomic_rmw => try self.airAtomicRmw(inst), - .atomic_load => try self.airAtomicLoad(inst), - .memcpy => try self.airMemcpy(inst), - .memset => try self.airMemset(inst), - .set_union_tag => try self.airSetUnionTag(inst), - .get_union_tag => try self.airGetUnionTag(inst), - .clz => try self.airClz(inst), - .ctz => try self.airCtz(inst), - .popcount => try self.airPopcount(inst), + .alloc => try self.airAlloc(inst), + .ret_ptr => try self.airRetPtr(inst), + .arg => try self.airArg(inst), + .assembly => try self.airAsm(inst), + .bitcast => try self.airBitCast(inst), + .block => try self.airBlock(inst), + .br => try self.airBr(inst), + .breakpoint => try self.airBreakpoint(), + .ret_addr => try self.airRetAddr(), + .fence => try self.airFence(), + .call => try self.airCall(inst), + .cond_br => try self.airCondBr(inst), + .dbg_stmt => try self.airDbgStmt(inst), + .fptrunc => try self.airFptrunc(inst), + .fpext => try self.airFpext(inst), + .intcast => try self.airIntCast(inst), + .trunc => try self.airTrunc(inst), + .bool_to_int => try self.airBoolToInt(inst), + .is_non_null => try self.airIsNonNull(inst), + .is_non_null_ptr => try self.airIsNonNullPtr(inst), + .is_null => try self.airIsNull(inst), + .is_null_ptr => try self.airIsNullPtr(inst), + .is_non_err => try self.airIsNonErr(inst), + .is_non_err_ptr => try self.airIsNonErrPtr(inst), + .is_err => try self.airIsErr(inst), + .is_err_ptr => try self.airIsErrPtr(inst), + .load => try self.airLoad(inst), + .loop => try self.airLoop(inst), + .not => try self.airNot(inst), + .ptrtoint => try self.airPtrToInt(inst), + .ret => try self.airRet(inst), + .ret_load => try self.airRetLoad(inst), + .store => try self.airStore(inst), + .struct_field_ptr=> try self.airStructFieldPtr(inst), + .struct_field_val=> try self.airStructFieldVal(inst), + .array_to_slice => try self.airArrayToSlice(inst), + .int_to_float => try self.airIntToFloat(inst), + .float_to_int => try self.airFloatToInt(inst), + .cmpxchg_strong => try self.airCmpxchg(inst), + .cmpxchg_weak => try self.airCmpxchg(inst), + .atomic_rmw => try self.airAtomicRmw(inst), + .atomic_load => try self.airAtomicLoad(inst), + .memcpy => try self.airMemcpy(inst), + .memset => try self.airMemset(inst), + .set_union_tag => try self.airSetUnionTag(inst), + .get_union_tag => try self.airGetUnionTag(inst), + .clz => try self.airClz(inst), + .ctz => try self.airCtz(inst), + .popcount => try self.airPopcount(inst), + .tag_name => try self.airTagName(inst), - .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), - .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), - .atomic_store_release => try self.airAtomicStore(inst, .Release), - .atomic_store_seq_cst => try self.airAtomicStore(inst, .SeqCst), + .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), + .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), + .atomic_store_release => try self.airAtomicStore(inst, .Release), + .atomic_store_seq_cst => try self.airAtomicStore(inst, .SeqCst), - .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0), - .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1), - .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2), - .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3), + .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0), + .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1), + .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2), + .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3), - .switch_br => try self.airSwitch(inst), - .slice_ptr => try self.airSlicePtr(inst), - .slice_len => try self.airSliceLen(inst), + .switch_br => try self.airSwitch(inst), + .slice_ptr => try self.airSlicePtr(inst), + .slice_len => try self.airSliceLen(inst), - .ptr_slice_len_ptr => try self.airPtrSliceLenPtr(inst), - .ptr_slice_ptr_ptr => try self.airPtrSlicePtrPtr(inst), + .ptr_slice_len_ptr => try self.airPtrSliceLenPtr(inst), + .ptr_slice_ptr_ptr => try self.airPtrSlicePtrPtr(inst), - .array_elem_val => try self.airArrayElemVal(inst), - .slice_elem_val => try self.airSliceElemVal(inst), - .slice_elem_ptr => try self.airSliceElemPtr(inst), - .ptr_elem_val => try self.airPtrElemVal(inst), - .ptr_elem_ptr => try self.airPtrElemPtr(inst), + .array_elem_val => try self.airArrayElemVal(inst), + .slice_elem_val => try self.airSliceElemVal(inst), + .slice_elem_ptr => try self.airSliceElemPtr(inst), + .ptr_elem_val => try self.airPtrElemVal(inst), + .ptr_elem_ptr => try self.airPtrElemPtr(inst), - .constant => unreachable, // excluded from function bodies - .const_ty => unreachable, // excluded from function bodies - .unreach => self.finishAirBookkeeping(), + .constant => unreachable, // excluded from function bodies + .const_ty => unreachable, // excluded from function bodies + .unreach => self.finishAirBookkeeping(), - .optional_payload => try self.airOptionalPayload(inst), - .optional_payload_ptr => try self.airOptionalPayloadPtr(inst), - .optional_payload_ptr_set => try self.airOptionalPayloadPtrSet(inst), - .unwrap_errunion_err => try self.airUnwrapErrErr(inst), - .unwrap_errunion_payload => try self.airUnwrapErrPayload(inst), - .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst), - .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst), + .optional_payload => try self.airOptionalPayload(inst), + .optional_payload_ptr => try self.airOptionalPayloadPtr(inst), + .optional_payload_ptr_set => try self.airOptionalPayloadPtrSet(inst), + .unwrap_errunion_err => try self.airUnwrapErrErr(inst), + .unwrap_errunion_payload => try self.airUnwrapErrPayload(inst), + .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst), + .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst), - .wrap_optional => try self.airWrapOptional(inst), - .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), - .wrap_errunion_err => try self.airWrapErrUnionErr(inst), - // zig fmt: on + .wrap_optional => try self.airWrapOptional(inst), + .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), + .wrap_errunion_err => try self.airWrapErrUnionErr(inst), + // zig fmt: on } if (std.debug.runtime_safety) { if (self.air_bookkeeping < old_air_bookkeeping + 1) { @@ -2045,6 +2046,16 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch}); } +fn airTagName(self: *Self, inst: Air.Inst.Index) !void { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const operand = try self.resolveInst(un_op); + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { + _ = operand; + return self.fail("TODO implement airTagName for riscv64", .{}); + }; + return self.finishAir(inst, result, .{ un_op, .none, .none }); +} + fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { // First section of indexes correspond to a set number of constant values. const ref_int = @enumToInt(inst); diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 6a87eeb97b..927e7827ea 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -538,133 +538,134 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { switch (air_tags[inst]) { // zig fmt: off - .add, .ptr_add => try self.airAdd(inst), - .addwrap => try self.airAddWrap(inst), - .add_sat => try self.airAddSat(inst), - .sub, .ptr_sub => try self.airSub(inst), - .subwrap => try self.airSubWrap(inst), - .sub_sat => try self.airSubSat(inst), - .mul => try self.airMul(inst), - .mulwrap => try self.airMulWrap(inst), - .mul_sat => try self.airMulSat(inst), - .rem => try self.airRem(inst), - .mod => try self.airMod(inst), - .shl, .shl_exact => try self.airShl(inst), - .shl_sat => try self.airShlSat(inst), - .min => try self.airMin(inst), - .max => try self.airMax(inst), - .slice => try self.airSlice(inst), + .add, .ptr_add => try self.airAdd(inst), + .addwrap => try self.airAddWrap(inst), + .add_sat => try self.airAddSat(inst), + .sub, .ptr_sub => try self.airSub(inst), + .subwrap => try self.airSubWrap(inst), + .sub_sat => try self.airSubSat(inst), + .mul => try self.airMul(inst), + .mulwrap => try self.airMulWrap(inst), + .mul_sat => try self.airMulSat(inst), + .rem => try self.airRem(inst), + .mod => try self.airMod(inst), + .shl, .shl_exact => try self.airShl(inst), + .shl_sat => try self.airShlSat(inst), + .min => try self.airMin(inst), + .max => try self.airMax(inst), + .slice => try self.airSlice(inst), - .add_with_overflow => try self.airAddWithOverflow(inst), - .sub_with_overflow => try self.airSubWithOverflow(inst), - .mul_with_overflow => try self.airMulWithOverflow(inst), - .shl_with_overflow => try self.airShlWithOverflow(inst), + .add_with_overflow => try self.airAddWithOverflow(inst), + .sub_with_overflow => try self.airSubWithOverflow(inst), + .mul_with_overflow => try self.airMulWithOverflow(inst), + .shl_with_overflow => try self.airShlWithOverflow(inst), - .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), + .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), - .cmp_lt => try self.airCmp(inst, .lt), - .cmp_lte => try self.airCmp(inst, .lte), - .cmp_eq => try self.airCmp(inst, .eq), - .cmp_gte => try self.airCmp(inst, .gte), - .cmp_gt => try self.airCmp(inst, .gt), - .cmp_neq => try self.airCmp(inst, .neq), + .cmp_lt => try self.airCmp(inst, .lt), + .cmp_lte => try self.airCmp(inst, .lte), + .cmp_eq => try self.airCmp(inst, .eq), + .cmp_gte => try self.airCmp(inst, .gte), + .cmp_gt => try self.airCmp(inst, .gt), + .cmp_neq => try self.airCmp(inst, .neq), - .bool_and => try self.airBoolOp(inst), - .bool_or => try self.airBoolOp(inst), - .bit_and => try self.airBitAnd(inst), - .bit_or => try self.airBitOr(inst), - .xor => try self.airXor(inst), - .shr => try self.airShr(inst), + .bool_and => try self.airBoolOp(inst), + .bool_or => try self.airBoolOp(inst), + .bit_and => try self.airBitAnd(inst), + .bit_or => try self.airBitOr(inst), + .xor => try self.airXor(inst), + .shr => try self.airShr(inst), - .alloc => try self.airAlloc(inst), - .ret_ptr => try self.airRetPtr(inst), - .arg => try self.airArg(inst), - .assembly => try self.airAsm(inst), - .bitcast => try self.airBitCast(inst), - .block => try self.airBlock(inst), - .br => try self.airBr(inst), - .breakpoint => try self.airBreakpoint(), - .ret_addr => try self.airRetAddr(), - .fence => try self.airFence(), - .call => try self.airCall(inst), - .cond_br => try self.airCondBr(inst), - .dbg_stmt => try self.airDbgStmt(inst), - .fptrunc => try self.airFptrunc(inst), - .fpext => try self.airFpext(inst), - .intcast => try self.airIntCast(inst), - .trunc => try self.airTrunc(inst), - .bool_to_int => try self.airBoolToInt(inst), - .is_non_null => try self.airIsNonNull(inst), - .is_non_null_ptr => try self.airIsNonNullPtr(inst), - .is_null => try self.airIsNull(inst), - .is_null_ptr => try self.airIsNullPtr(inst), - .is_non_err => try self.airIsNonErr(inst), - .is_non_err_ptr => try self.airIsNonErrPtr(inst), - .is_err => try self.airIsErr(inst), - .is_err_ptr => try self.airIsErrPtr(inst), - .load => try self.airLoad(inst), - .loop => try self.airLoop(inst), - .not => try self.airNot(inst), - .ptrtoint => try self.airPtrToInt(inst), - .ret => try self.airRet(inst), - .ret_load => try self.airRetLoad(inst), - .store => try self.airStore(inst), - .struct_field_ptr=> try self.airStructFieldPtr(inst), - .struct_field_val=> try self.airStructFieldVal(inst), - .array_to_slice => try self.airArrayToSlice(inst), - .int_to_float => try self.airIntToFloat(inst), - .float_to_int => try self.airFloatToInt(inst), - .cmpxchg_strong => try self.airCmpxchg(inst), - .cmpxchg_weak => try self.airCmpxchg(inst), - .atomic_rmw => try self.airAtomicRmw(inst), - .atomic_load => try self.airAtomicLoad(inst), - .memcpy => try self.airMemcpy(inst), - .memset => try self.airMemset(inst), - .set_union_tag => try self.airSetUnionTag(inst), - .get_union_tag => try self.airGetUnionTag(inst), - .clz => try self.airClz(inst), - .ctz => try self.airCtz(inst), - .popcount => try self.airPopcount(inst), + .alloc => try self.airAlloc(inst), + .ret_ptr => try self.airRetPtr(inst), + .arg => try self.airArg(inst), + .assembly => try self.airAsm(inst), + .bitcast => try self.airBitCast(inst), + .block => try self.airBlock(inst), + .br => try self.airBr(inst), + .breakpoint => try self.airBreakpoint(), + .ret_addr => try self.airRetAddr(), + .fence => try self.airFence(), + .call => try self.airCall(inst), + .cond_br => try self.airCondBr(inst), + .dbg_stmt => try self.airDbgStmt(inst), + .fptrunc => try self.airFptrunc(inst), + .fpext => try self.airFpext(inst), + .intcast => try self.airIntCast(inst), + .trunc => try self.airTrunc(inst), + .bool_to_int => try self.airBoolToInt(inst), + .is_non_null => try self.airIsNonNull(inst), + .is_non_null_ptr => try self.airIsNonNullPtr(inst), + .is_null => try self.airIsNull(inst), + .is_null_ptr => try self.airIsNullPtr(inst), + .is_non_err => try self.airIsNonErr(inst), + .is_non_err_ptr => try self.airIsNonErrPtr(inst), + .is_err => try self.airIsErr(inst), + .is_err_ptr => try self.airIsErrPtr(inst), + .load => try self.airLoad(inst), + .loop => try self.airLoop(inst), + .not => try self.airNot(inst), + .ptrtoint => try self.airPtrToInt(inst), + .ret => try self.airRet(inst), + .ret_load => try self.airRetLoad(inst), + .store => try self.airStore(inst), + .struct_field_ptr=> try self.airStructFieldPtr(inst), + .struct_field_val=> try self.airStructFieldVal(inst), + .array_to_slice => try self.airArrayToSlice(inst), + .int_to_float => try self.airIntToFloat(inst), + .float_to_int => try self.airFloatToInt(inst), + .cmpxchg_strong => try self.airCmpxchg(inst), + .cmpxchg_weak => try self.airCmpxchg(inst), + .atomic_rmw => try self.airAtomicRmw(inst), + .atomic_load => try self.airAtomicLoad(inst), + .memcpy => try self.airMemcpy(inst), + .memset => try self.airMemset(inst), + .set_union_tag => try self.airSetUnionTag(inst), + .get_union_tag => try self.airGetUnionTag(inst), + .clz => try self.airClz(inst), + .ctz => try self.airCtz(inst), + .popcount => try self.airPopcount(inst), + .tag_name => try self.airTagName(inst), - .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), - .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), - .atomic_store_release => try self.airAtomicStore(inst, .Release), - .atomic_store_seq_cst => try self.airAtomicStore(inst, .SeqCst), + .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), + .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), + .atomic_store_release => try self.airAtomicStore(inst, .Release), + .atomic_store_seq_cst => try self.airAtomicStore(inst, .SeqCst), - .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0), - .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1), - .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2), - .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3), + .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0), + .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1), + .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2), + .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3), - .switch_br => try self.airSwitch(inst), - .slice_ptr => try self.airSlicePtr(inst), - .slice_len => try self.airSliceLen(inst), + .switch_br => try self.airSwitch(inst), + .slice_ptr => try self.airSlicePtr(inst), + .slice_len => try self.airSliceLen(inst), - .ptr_slice_len_ptr => try self.airPtrSliceLenPtr(inst), - .ptr_slice_ptr_ptr => try self.airPtrSlicePtrPtr(inst), + .ptr_slice_len_ptr => try self.airPtrSliceLenPtr(inst), + .ptr_slice_ptr_ptr => try self.airPtrSlicePtrPtr(inst), - .array_elem_val => try self.airArrayElemVal(inst), - .slice_elem_val => try self.airSliceElemVal(inst), - .slice_elem_ptr => try self.airSliceElemPtr(inst), - .ptr_elem_val => try self.airPtrElemVal(inst), - .ptr_elem_ptr => try self.airPtrElemPtr(inst), + .array_elem_val => try self.airArrayElemVal(inst), + .slice_elem_val => try self.airSliceElemVal(inst), + .slice_elem_ptr => try self.airSliceElemPtr(inst), + .ptr_elem_val => try self.airPtrElemVal(inst), + .ptr_elem_ptr => try self.airPtrElemPtr(inst), - .constant => unreachable, // excluded from function bodies - .const_ty => unreachable, // excluded from function bodies - .unreach => self.finishAirBookkeeping(), + .constant => unreachable, // excluded from function bodies + .const_ty => unreachable, // excluded from function bodies + .unreach => self.finishAirBookkeeping(), - .optional_payload => try self.airOptionalPayload(inst), - .optional_payload_ptr => try self.airOptionalPayloadPtr(inst), - .optional_payload_ptr_set => try self.airOptionalPayloadPtrSet(inst), - .unwrap_errunion_err => try self.airUnwrapErrErr(inst), - .unwrap_errunion_payload => try self.airUnwrapErrPayload(inst), - .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst), - .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst), + .optional_payload => try self.airOptionalPayload(inst), + .optional_payload_ptr => try self.airOptionalPayloadPtr(inst), + .optional_payload_ptr_set => try self.airOptionalPayloadPtrSet(inst), + .unwrap_errunion_err => try self.airUnwrapErrErr(inst), + .unwrap_errunion_payload => try self.airUnwrapErrPayload(inst), + .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst), + .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst), - .wrap_optional => try self.airWrapOptional(inst), - .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), - .wrap_errunion_err => try self.airWrapErrUnionErr(inst), - // zig fmt: on + .wrap_optional => try self.airWrapOptional(inst), + .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), + .wrap_errunion_err => try self.airWrapErrUnionErr(inst), + // zig fmt: on } if (std.debug.runtime_safety) { if (self.air_bookkeeping < old_air_bookkeeping + 1) { @@ -3174,6 +3175,16 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch}); } +fn airTagName(self: *Self, inst: Air.Inst.Index) !void { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const operand = try self.resolveInst(un_op); + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { + _ = operand; + return self.fail("TODO implement airTagName for x86_64", .{}); + }; + return self.finishAir(inst, result, .{ un_op, .none, .none }); +} + fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { // First section of indexes correspond to a set number of constant values. const ref_int = @enumToInt(inst); diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 86ec58457f..56df9a86cb 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1230,6 +1230,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .clz => try airBuiltinCall(f, inst, "clz"), .ctz => try airBuiltinCall(f, inst, "ctz"), .popcount => try airBuiltinCall(f, inst, "popcount"), + .tag_name => try airTagName(f, inst), .int_to_float, .float_to_int, @@ -2914,6 +2915,24 @@ fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { return local; } +fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue { + if (f.liveness.isUnused(inst)) return CValue.none; + + const un_op = f.air.instructions.items(.data)[inst].un_op; + const writer = f.object.writer(); + const inst_ty = f.air.typeOfIndex(inst); + const operand = try f.resolveInst(un_op); + const local = try f.allocLocal(inst_ty, .Const); + + try writer.writeAll(" = "); + + _ = operand; + _ = local; + return f.fail("TODO: C backend: implement airTagName", .{}); + //try writer.writeAll(";\n"); + //return local; +} + fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 { return switch (order) { .Unordered => "memory_order_relaxed", diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 8e274e2a43..22361a1c6e 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -636,15 +636,6 @@ pub const DeclGen = struct { llvm_param_i += 1; } - if (dg.module.comp.bin_file.options.skip_linker_dependencies) { - // The intent here is for compiler-rt and libc functions to not generate - // infinite recursion. For example, if we are compiling the memcpy function, - // and llvm detects that the body is equivalent to memcpy, it may replace the - // body of memcpy with a call to memcpy, which would then cause a stack - // overflow instead of performing memcpy. - dg.addFnAttr(llvm_fn, "nobuiltin"); - } - // TODO: more attributes. see codegen.cpp `make_fn_llvm_value`. if (fn_info.cc == .Naked) { dg.addFnAttr(llvm_fn, "naked"); @@ -653,6 +644,16 @@ pub const DeclGen = struct { } // Function attributes that are independent of analysis results of the function body. + dg.addCommonFnAttributes(llvm_fn); + + if (return_type.isNoReturn()) { + dg.addFnAttr(llvm_fn, "noreturn"); + } + + return llvm_fn; + } + + fn addCommonFnAttributes(dg: *DeclGen, llvm_fn: *const llvm.Value) void { if (!dg.module.comp.bin_file.options.red_zone) { dg.addFnAttr(llvm_fn, "noredzone"); } @@ -665,6 +666,14 @@ pub const DeclGen = struct { if (dg.module.comp.unwind_tables) { dg.addFnAttr(llvm_fn, "uwtable"); } + if (dg.module.comp.bin_file.options.skip_linker_dependencies) { + // The intent here is for compiler-rt and libc functions to not generate + // infinite recursion. For example, if we are compiling the memcpy function, + // and llvm detects that the body is equivalent to memcpy, it may replace the + // body of memcpy with a call to memcpy, which would then cause a stack + // overflow instead of performing memcpy. + dg.addFnAttr(llvm_fn, "nobuiltin"); + } if (dg.module.comp.bin_file.options.optimize_mode == .ReleaseSmall) { dg.addFnAttr(llvm_fn, "minsize"); dg.addFnAttr(llvm_fn, "optsize"); @@ -673,11 +682,6 @@ pub const DeclGen = struct { dg.addFnAttr(llvm_fn, "sanitize_thread"); } // TODO add target-cpu and target-features fn attributes - if (return_type.isNoReturn()) { - dg.addFnAttr(llvm_fn, "noreturn"); - } - - return llvm_fn; } fn resolveGlobalDecl(dg: *DeclGen, decl: *Module.Decl) Error!*const llvm.Value { @@ -1958,6 +1962,7 @@ pub const FuncGen = struct { .clz => try self.airClzCtz(inst, "ctlz"), .ctz => try self.airClzCtz(inst, "cttz"), .popcount => try self.airPopCount(inst, "ctpop"), + .tag_name => try self.airTagName(inst), .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), @@ -4093,6 +4098,119 @@ pub const FuncGen = struct { } } + fn airTagName(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) return null; + + var arena_allocator = std.heap.ArenaAllocator.init(self.gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + + const un_op = self.air.instructions.items(.data)[inst].un_op; + const operand = try self.resolveInst(un_op); + const enum_ty = self.air.typeOf(un_op); + + const llvm_fn_name = try std.fmt.allocPrintZ(arena, "__zig_tag_name_{s}", .{ + try enum_ty.getOwnerDecl().getFullyQualifiedName(arena), + }); + + const llvm_fn = try self.getEnumTagNameFunction(enum_ty, llvm_fn_name); + const params = [_]*const llvm.Value{operand}; + return self.builder.buildCall(llvm_fn, ¶ms, params.len, .Fast, .Auto, ""); + } + + fn getEnumTagNameFunction( + self: *FuncGen, + enum_ty: Type, + llvm_fn_name: [:0]const u8, + ) !*const llvm.Value { + // TODO: detect when the type changes and re-emit this function. + if (self.dg.object.llvm_module.getNamedFunction(llvm_fn_name)) |llvm_fn| { + return llvm_fn; + } + + const slice_ty = Type.initTag(.const_slice_u8_sentinel_0); + const llvm_ret_ty = try self.dg.llvmType(slice_ty); + const usize_llvm_ty = try self.dg.llvmType(Type.usize); + const target = self.dg.module.getTarget(); + const slice_alignment = slice_ty.abiAlignment(target); + + var int_tag_type_buffer: Type.Payload.Bits = undefined; + const int_tag_ty = enum_ty.intTagType(&int_tag_type_buffer); + const param_types = [_]*const llvm.Type{try self.dg.llvmType(int_tag_ty)}; + + const fn_type = llvm.functionType(llvm_ret_ty, ¶m_types, param_types.len, .False); + const fn_val = self.dg.object.llvm_module.addFunction(llvm_fn_name, fn_type); + fn_val.setLinkage(.Internal); + fn_val.setFunctionCallConv(.Fast); + self.dg.addCommonFnAttributes(fn_val); + + const prev_block = self.builder.getInsertBlock(); + const prev_debug_location = self.builder.getCurrentDebugLocation2(); + defer { + self.builder.positionBuilderAtEnd(prev_block); + if (!self.dg.module.comp.bin_file.options.strip) { + self.builder.setCurrentDebugLocation2(prev_debug_location); + } + } + + const entry_block = self.dg.context.appendBasicBlock(fn_val, "Entry"); + self.builder.positionBuilderAtEnd(entry_block); + self.builder.clearCurrentDebugLocation(); + + const fields = enum_ty.enumFields(); + const bad_value_block = self.dg.context.appendBasicBlock(fn_val, "BadValue"); + const tag_int_value = fn_val.getParam(0); + const switch_instr = self.builder.buildSwitch(tag_int_value, bad_value_block, @intCast(c_uint, fields.count())); + + const array_ptr_indices = [_]*const llvm.Value{ + usize_llvm_ty.constNull(), usize_llvm_ty.constNull(), + }; + + for (fields.keys()) |name, field_index| { + const str_init = self.dg.context.constString(name.ptr, @intCast(c_uint, name.len), .False); + const str_global = self.dg.object.llvm_module.addGlobal(str_init.typeOf(), ""); + str_global.setInitializer(str_init); + str_global.setLinkage(.Private); + str_global.setGlobalConstant(.True); + str_global.setUnnamedAddr(.True); + str_global.setAlignment(1); + + const slice_fields = [_]*const llvm.Value{ + str_global.constInBoundsGEP(&array_ptr_indices, array_ptr_indices.len), + usize_llvm_ty.constInt(name.len, .False), + }; + const slice_init = llvm_ret_ty.constNamedStruct(&slice_fields, slice_fields.len); + const slice_global = self.dg.object.llvm_module.addGlobal(slice_init.typeOf(), ""); + slice_global.setInitializer(slice_init); + slice_global.setLinkage(.Private); + slice_global.setGlobalConstant(.True); + slice_global.setUnnamedAddr(.True); + slice_global.setAlignment(slice_alignment); + + const return_block = self.dg.context.appendBasicBlock(fn_val, "Name"); + const this_tag_int_value = int: { + var tag_val_payload: Value.Payload.U32 = .{ + .base = .{ .tag = .enum_field_index }, + .data = @intCast(u32, field_index), + }; + break :int try self.dg.genTypedValue(.{ + .ty = enum_ty, + .val = Value.initPayload(&tag_val_payload.base), + }); + }; + switch_instr.addCase(this_tag_int_value, return_block); + + self.builder.positionBuilderAtEnd(return_block); + const loaded = self.builder.buildLoad(slice_global, ""); + loaded.setAlignment(slice_alignment); + _ = self.builder.buildRet(loaded); + } + + self.builder.positionBuilderAtEnd(bad_value_block); + _ = self.builder.buildUnreachable(); + return fn_val; + } + /// Assumes the optional is not pointer-like and payload has bits. fn optIsNonNull(self: *FuncGen, opt_handle: *const llvm.Value, is_by_ref: bool) *const llvm.Value { if (is_by_ref) { diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index 36e5bf060a..e6a61ca883 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -785,8 +785,23 @@ pub const Builder = opaque { pub const buildExactSDiv = LLVMBuildExactSDiv; extern fn LLVMBuildExactSDiv(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + + pub const zigSetCurrentDebugLocation = ZigLLVMSetCurrentDebugLocation; + extern fn ZigLLVMSetCurrentDebugLocation(builder: *const Builder, line: c_int, column: c_int, scope: *DIScope) void; + + pub const clearCurrentDebugLocation = ZigLLVMClearCurrentDebugLocation; + extern fn ZigLLVMClearCurrentDebugLocation(builder: *const Builder) void; + + pub const getCurrentDebugLocation2 = LLVMGetCurrentDebugLocation2; + extern fn LLVMGetCurrentDebugLocation2(Builder: *const Builder) *Metadata; + + pub const setCurrentDebugLocation2 = LLVMSetCurrentDebugLocation2; + extern fn LLVMSetCurrentDebugLocation2(Builder: *const Builder, Loc: *Metadata) void; }; +pub const DIScope = opaque {}; +pub const Metadata = opaque {}; + pub const IntPredicate = enum(c_uint) { EQ = 32, NE = 33, diff --git a/src/print_air.zig b/src/print_air.zig index b3a1be28f4..cc4acfa279 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -155,6 +155,7 @@ const Writer = struct { .bool_to_int, .ret, .ret_load, + .tag_name, => try w.writeUnOp(s, inst), .breakpoint, diff --git a/src/type.zig b/src/type.zig index b463604340..1482710e51 100644 --- a/src/type.zig +++ b/src/type.zig @@ -94,6 +94,7 @@ pub const Type = extern union { .single_const_pointer_to_comptime_int, .const_slice_u8, + .const_slice_u8_sentinel_0, .single_const_pointer, .single_mut_pointer, .many_const_pointer, @@ -107,6 +108,7 @@ pub const Type = extern union { .inferred_alloc_mut, .manyptr_u8, .manyptr_const_u8, + .manyptr_const_u8_sentinel_0, => return .Pointer, .optional, @@ -254,6 +256,7 @@ pub const Type = extern union { .optional_single_mut_pointer, .manyptr_u8, .manyptr_const_u8, + .manyptr_const_u8_sentinel_0, => self.cast(Payload.ElemType), .inferred_alloc_const => unreachable, @@ -275,9 +278,11 @@ pub const Type = extern union { return switch (ty.tag()) { .single_const_pointer_to_comptime_int, .const_slice_u8, + .const_slice_u8_sentinel_0, .single_const_pointer, .many_const_pointer, .manyptr_const_u8, + .manyptr_const_u8_sentinel_0, .c_const_pointer, .const_slice, => false, @@ -330,6 +335,18 @@ pub const Type = extern union { .@"volatile" = false, .size = .Slice, } }, + .const_slice_u8_sentinel_0 => return .{ .data = .{ + .pointee_type = Type.initTag(.u8), + .sentinel = Value.zero, + .@"align" = 0, + .@"addrspace" = .generic, + .bit_offset = 0, + .host_size = 0, + .@"allowzero" = false, + .mutable = false, + .@"volatile" = false, + .size = .Slice, + } }, .single_const_pointer => return .{ .data = .{ .pointee_type = self.castPointer().?.data, .sentinel = null, @@ -378,6 +395,18 @@ pub const Type = extern union { .@"volatile" = false, .size = .Many, } }, + .manyptr_const_u8_sentinel_0 => return .{ .data = .{ + .pointee_type = Type.initTag(.u8), + .sentinel = Value.zero, + .@"align" = 0, + .@"addrspace" = .generic, + .bit_offset = 0, + .host_size = 0, + .@"allowzero" = false, + .mutable = false, + .@"volatile" = false, + .size = .Many, + } }, .many_mut_pointer => return .{ .data = .{ .pointee_type = self.castPointer().?.data, .sentinel = null, @@ -784,6 +813,7 @@ pub const Type = extern union { .fn_ccc_void_no_args, .single_const_pointer_to_comptime_int, .const_slice_u8, + .const_slice_u8_sentinel_0, .enum_literal, .anyerror_void_error_union, .inferred_alloc_const, @@ -792,6 +822,7 @@ pub const Type = extern union { .empty_struct_literal, .manyptr_u8, .manyptr_const_u8, + .manyptr_const_u8_sentinel_0, .atomic_order, .atomic_rmw_op, .calling_convention, @@ -1016,6 +1047,7 @@ pub const Type = extern union { .anyerror_void_error_union => return writer.writeAll("anyerror!void"), .const_slice_u8 => return writer.writeAll("[]const u8"), + .const_slice_u8_sentinel_0 => return writer.writeAll("[:0]const u8"), .fn_noreturn_no_args => return writer.writeAll("fn() noreturn"), .fn_void_no_args => return writer.writeAll("fn() void"), .fn_naked_noreturn_no_args => return writer.writeAll("fn() callconv(.Naked) noreturn"), @@ -1023,6 +1055,7 @@ pub const Type = extern union { .single_const_pointer_to_comptime_int => return writer.writeAll("*const comptime_int"), .manyptr_u8 => return writer.writeAll("[*]u8"), .manyptr_const_u8 => return writer.writeAll("[*]const u8"), + .manyptr_const_u8_sentinel_0 => return writer.writeAll("[*:0]const u8"), .atomic_order => return writer.writeAll("std.builtin.AtomicOrder"), .atomic_rmw_op => return writer.writeAll("std.builtin.AtomicRmwOp"), .calling_convention => return writer.writeAll("std.builtin.CallingConvention"), @@ -1308,6 +1341,7 @@ pub const Type = extern union { .anyerror_void_error_union => return "anyerror!void", .const_slice_u8 => return "[]const u8", + .const_slice_u8_sentinel_0 => return "[:0]const u8", .fn_noreturn_no_args => return "fn() noreturn", .fn_void_no_args => return "fn() void", .fn_naked_noreturn_no_args => return "fn() callconv(.Naked) noreturn", @@ -1315,6 +1349,7 @@ pub const Type = extern union { .single_const_pointer_to_comptime_int => return "*const comptime_int", .manyptr_u8 => return "[*]u8", .manyptr_const_u8 => return "[*]const u8", + .manyptr_const_u8_sentinel_0 => return "[*:0]const u8", .atomic_order => return "AtomicOrder", .atomic_rmw_op => return "AtomicRmwOp", .calling_convention => return "CallingConvention", @@ -1386,11 +1421,13 @@ pub const Type = extern union { .extern_options, .manyptr_u8, .manyptr_const_u8, + .manyptr_const_u8_sentinel_0, .fn_noreturn_no_args, .fn_void_no_args, .fn_naked_noreturn_no_args, .fn_ccc_void_no_args, .const_slice_u8, + .const_slice_u8_sentinel_0, .anyerror_void_error_union, .empty_struct_literal, .function, @@ -1498,9 +1535,11 @@ pub const Type = extern union { .fn_ccc_void_no_args => return Value.initTag(.fn_ccc_void_no_args_type), .single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type), .const_slice_u8 => return Value.initTag(.const_slice_u8_type), + .const_slice_u8_sentinel_0 => return Value.initTag(.const_slice_u8_sentinel_0_type), .enum_literal => return Value.initTag(.enum_literal_type), .manyptr_u8 => return Value.initTag(.manyptr_u8_type), .manyptr_const_u8 => return Value.initTag(.manyptr_const_u8_type), + .manyptr_const_u8_sentinel_0 => return Value.initTag(.manyptr_const_u8_sentinel_0_type), .atomic_order => return Value.initTag(.atomic_order_type), .atomic_rmw_op => return Value.initTag(.atomic_rmw_op_type), .calling_convention => return Value.initTag(.calling_convention_type), @@ -1550,6 +1589,7 @@ pub const Type = extern union { .anyerror, .single_const_pointer_to_comptime_int, .const_slice_u8, + .const_slice_u8_sentinel_0, .array_u8_sentinel_0, .optional, .optional_single_mut_pointer, @@ -1561,6 +1601,7 @@ pub const Type = extern union { .error_set_merged, .manyptr_u8, .manyptr_const_u8, + .manyptr_const_u8_sentinel_0, .atomic_order, .atomic_rmw_op, .calling_convention, @@ -1703,7 +1744,9 @@ pub const Type = extern union { .manyptr_u8, .manyptr_const_u8, + .manyptr_const_u8_sentinel_0, .const_slice_u8, + .const_slice_u8_sentinel_0, => return 1, .pointer => { @@ -1723,6 +1766,7 @@ pub const Type = extern union { return switch (self.tag()) { .single_const_pointer_to_comptime_int, .const_slice_u8, + .const_slice_u8_sentinel_0, .single_const_pointer, .single_mut_pointer, .many_const_pointer, @@ -1735,6 +1779,7 @@ pub const Type = extern union { .inferred_alloc_mut, .manyptr_u8, .manyptr_const_u8, + .manyptr_const_u8_sentinel_0, => .generic, .pointer => self.castTag(.pointer).?.data.@"addrspace", @@ -1785,6 +1830,7 @@ pub const Type = extern union { .usize, .single_const_pointer_to_comptime_int, .const_slice_u8, + .const_slice_u8_sentinel_0, .single_const_pointer, .single_mut_pointer, .many_const_pointer, @@ -1798,6 +1844,7 @@ pub const Type = extern union { .pointer, .manyptr_u8, .manyptr_const_u8, + .manyptr_const_u8_sentinel_0, .@"anyframe", .anyframe_T, => return @divExact(target.cpu.arch.ptrBitWidth(), 8), @@ -2050,7 +2097,9 @@ pub const Type = extern union { if (self.elemType().hasCodeGenBits()) return @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2; return @divExact(target.cpu.arch.ptrBitWidth(), 8); }, - .const_slice_u8 => return @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2, + .const_slice_u8, + .const_slice_u8_sentinel_0, + => return @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2, .optional_single_const_pointer, .optional_single_mut_pointer, @@ -2068,6 +2117,7 @@ pub const Type = extern union { .pointer, .manyptr_u8, .manyptr_const_u8, + .manyptr_const_u8_sentinel_0, => return @divExact(target.cpu.arch.ptrBitWidth(), 8), .c_short => return @divExact(CType.short.sizeInBits(target), 8), @@ -2223,7 +2273,9 @@ pub const Type = extern union { return target.cpu.arch.ptrBitWidth(); } }, - .const_slice_u8 => target.cpu.arch.ptrBitWidth() * 2, + .const_slice_u8, + .const_slice_u8_sentinel_0, + => target.cpu.arch.ptrBitWidth() * 2, .optional_single_const_pointer, .optional_single_mut_pointer, @@ -2252,6 +2304,7 @@ pub const Type = extern union { .manyptr_u8, .manyptr_const_u8, + .manyptr_const_u8_sentinel_0, => return target.cpu.arch.ptrBitWidth(), .c_short => return CType.short.sizeInBits(target), @@ -2337,12 +2390,14 @@ pub const Type = extern union { .const_slice, .mut_slice, .const_slice_u8, + .const_slice_u8_sentinel_0, => .Slice, .many_const_pointer, .many_mut_pointer, .manyptr_u8, .manyptr_const_u8, + .manyptr_const_u8_sentinel_0, => .Many, .c_const_pointer, @@ -2367,6 +2422,7 @@ pub const Type = extern union { .const_slice, .mut_slice, .const_slice_u8, + .const_slice_u8_sentinel_0, => true, .pointer => self.castTag(.pointer).?.data.size == .Slice, @@ -2383,6 +2439,7 @@ pub const Type = extern union { pub fn slicePtrFieldType(self: Type, buffer: *SlicePtrFieldTypeBuffer) Type { switch (self.tag()) { .const_slice_u8 => return Type.initTag(.manyptr_const_u8), + .const_slice_u8_sentinel_0 => return Type.initTag(.manyptr_const_u8_sentinel_0), .const_slice => { const elem_type = self.castTag(.const_slice).?.data; @@ -2464,8 +2521,10 @@ pub const Type = extern union { .c_const_pointer, .single_const_pointer_to_comptime_int, .const_slice_u8, + .const_slice_u8_sentinel_0, .const_slice, .manyptr_const_u8, + .manyptr_const_u8_sentinel_0, => true, .pointer => !self.castTag(.pointer).?.data.mutable, @@ -2513,6 +2572,7 @@ pub const Type = extern union { .many_const_pointer, .many_mut_pointer, .manyptr_const_u8, + .manyptr_const_u8_sentinel_0, .manyptr_u8, .optional_single_const_pointer, .optional_single_mut_pointer, @@ -2648,9 +2708,11 @@ pub const Type = extern union { .array_u8, .array_u8_sentinel_0, .const_slice_u8, + .const_slice_u8_sentinel_0, .manyptr_u8, .manyptr_const_u8, - => Type.initTag(.u8), + .manyptr_const_u8_sentinel_0, + => Type.u8, .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int), .pointer => ty.castTag(.pointer).?.data.pointee_type, @@ -2690,9 +2752,11 @@ pub const Type = extern union { .array_u8, .array_u8_sentinel_0, .const_slice_u8, + .const_slice_u8_sentinel_0, .manyptr_u8, .manyptr_const_u8, - => Type.initTag(.u8), + .manyptr_const_u8_sentinel_0, + => Type.u8, .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int), .pointer => { @@ -2937,7 +3001,11 @@ pub const Type = extern union { .pointer => return self.castTag(.pointer).?.data.sentinel, .array_sentinel => return self.castTag(.array_sentinel).?.data.sentinel, - .array_u8_sentinel_0 => return Value.zero, + + .array_u8_sentinel_0, + .const_slice_u8_sentinel_0, + .manyptr_const_u8_sentinel_0, + => return Value.zero, else => unreachable, }; @@ -3309,6 +3377,7 @@ pub const Type = extern union { .array_sentinel, .array_u8_sentinel_0, .const_slice_u8, + .const_slice_u8_sentinel_0, .const_slice, .mut_slice, .anyopaque, @@ -3326,6 +3395,7 @@ pub const Type = extern union { .var_args_param, .manyptr_u8, .manyptr_const_u8, + .manyptr_const_u8_sentinel_0, .atomic_order, .atomic_rmw_op, .calling_convention, @@ -3956,12 +4026,14 @@ pub const Type = extern union { type_info, manyptr_u8, manyptr_const_u8, + manyptr_const_u8_sentinel_0, fn_noreturn_no_args, fn_void_no_args, fn_naked_noreturn_no_args, fn_ccc_void_no_args, single_const_pointer_to_comptime_int, const_slice_u8, + const_slice_u8_sentinel_0, anyerror_void_error_union, generic_poison, /// This is a special type for variadic parameters of a function call. @@ -4064,6 +4136,7 @@ pub const Type = extern union { .single_const_pointer_to_comptime_int, .anyerror_void_error_union, .const_slice_u8, + .const_slice_u8_sentinel_0, .generic_poison, .inferred_alloc_const, .inferred_alloc_mut, @@ -4071,6 +4144,7 @@ pub const Type = extern union { .empty_struct_literal, .manyptr_u8, .manyptr_const_u8, + .manyptr_const_u8_sentinel_0, .atomic_order, .atomic_rmw_op, .calling_convention, @@ -4322,36 +4396,55 @@ pub const Type = extern union { pub fn ptr(arena: Allocator, d: Payload.Pointer.Data) !Type { assert(d.host_size == 0 or d.bit_offset < d.host_size * 8); + if (d.size == .C) { + assert(d.@"allowzero"); // All C pointers must set allowzero to true. + } - if (d.sentinel != null or d.@"align" != 0 or d.@"addrspace" != .generic or - d.bit_offset != 0 or d.host_size != 0 or d.@"allowzero" or d.@"volatile") + if (d.@"align" == 0 and d.@"addrspace" == .generic and + d.bit_offset == 0 and d.host_size == 0 and !d.@"allowzero" and !d.@"volatile") { - if (d.size == .C) { - assert(d.@"allowzero"); // All C pointers must set allowzero to true. + if (d.sentinel) |sent| { + if (!d.mutable and d.pointee_type.eql(Type.u8)) { + switch (d.size) { + .Slice => { + if (sent.compareWithZero(.eq)) { + return Type.initTag(.const_slice_u8_sentinel_0); + } + }, + .Many => { + if (sent.compareWithZero(.eq)) { + return Type.initTag(.manyptr_const_u8_sentinel_0); + } + }, + else => {}, + } + } + } else if (!d.mutable and d.pointee_type.eql(Type.u8)) { + switch (d.size) { + .Slice => return Type.initTag(.const_slice_u8), + .Many => return Type.initTag(.manyptr_const_u8), + else => {}, + } + } else { + // TODO stage1 type inference bug + const T = Type.Tag; + + const type_payload = try arena.create(Type.Payload.ElemType); + type_payload.* = .{ + .base = .{ + .tag = switch (d.size) { + .One => if (d.mutable) T.single_mut_pointer else T.single_const_pointer, + .Many => if (d.mutable) T.many_mut_pointer else T.many_const_pointer, + .C => if (d.mutable) T.c_mut_pointer else T.c_const_pointer, + .Slice => if (d.mutable) T.mut_slice else T.const_slice, + }, + }, + .data = d.pointee_type, + }; + return Type.initPayload(&type_payload.base); } - return Type.Tag.pointer.create(arena, d); } - - if (!d.mutable and d.size == .Slice and d.pointee_type.eql(Type.initTag(.u8))) { - return Type.initTag(.const_slice_u8); - } - - // TODO stage1 type inference bug - const T = Type.Tag; - - const type_payload = try arena.create(Type.Payload.ElemType); - type_payload.* = .{ - .base = .{ - .tag = switch (d.size) { - .One => if (d.mutable) T.single_mut_pointer else T.single_const_pointer, - .Many => if (d.mutable) T.many_mut_pointer else T.many_const_pointer, - .C => if (d.mutable) T.c_mut_pointer else T.c_const_pointer, - .Slice => if (d.mutable) T.mut_slice else T.const_slice, - }, - }, - .data = d.pointee_type, - }; - return Type.initPayload(&type_payload.base); + return Type.Tag.pointer.create(arena, d); } pub fn array( diff --git a/src/value.zig b/src/value.zig index be26e38d52..ac4f64cada 100644 --- a/src/value.zig +++ b/src/value.zig @@ -73,12 +73,14 @@ pub const Value = extern union { type_info_type, manyptr_u8_type, manyptr_const_u8_type, + manyptr_const_u8_sentinel_0_type, fn_noreturn_no_args_type, fn_void_no_args_type, fn_naked_noreturn_no_args_type, fn_ccc_void_no_args_type, single_const_pointer_to_comptime_int_type, const_slice_u8_type, + const_slice_u8_sentinel_0_type, anyerror_void_error_union_type, generic_poison_type, @@ -221,6 +223,7 @@ pub const Value = extern union { .single_const_pointer_to_comptime_int_type, .anyframe_type, .const_slice_u8_type, + .const_slice_u8_sentinel_0_type, .anyerror_void_error_union_type, .generic_poison_type, .enum_literal_type, @@ -238,6 +241,7 @@ pub const Value = extern union { .abi_align_default, .manyptr_u8_type, .manyptr_const_u8_type, + .manyptr_const_u8_sentinel_0_type, .atomic_order_type, .atomic_rmw_op_type, .calling_convention_type, @@ -412,6 +416,7 @@ pub const Value = extern union { .single_const_pointer_to_comptime_int_type, .anyframe_type, .const_slice_u8_type, + .const_slice_u8_sentinel_0_type, .anyerror_void_error_union_type, .generic_poison_type, .enum_literal_type, @@ -429,6 +434,7 @@ pub const Value = extern union { .abi_align_default, .manyptr_u8_type, .manyptr_const_u8_type, + .manyptr_const_u8_sentinel_0_type, .atomic_order_type, .atomic_rmw_op_type, .calling_convention_type, @@ -642,12 +648,14 @@ pub const Value = extern union { .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"), .anyframe_type => return out_stream.writeAll("anyframe"), .const_slice_u8_type => return out_stream.writeAll("[]const u8"), + .const_slice_u8_sentinel_0_type => return out_stream.writeAll("[:0]const u8"), .anyerror_void_error_union_type => return out_stream.writeAll("anyerror!void"), .generic_poison_type => return out_stream.writeAll("(generic poison type)"), .generic_poison => return out_stream.writeAll("(generic poison)"), .enum_literal_type => return out_stream.writeAll("@Type(.EnumLiteral)"), .manyptr_u8_type => return out_stream.writeAll("[*]u8"), .manyptr_const_u8_type => return out_stream.writeAll("[*]const u8"), + .manyptr_const_u8_sentinel_0_type => return out_stream.writeAll("[*:0]const u8"), .atomic_order_type => return out_stream.writeAll("std.builtin.AtomicOrder"), .atomic_rmw_op_type => return out_stream.writeAll("std.builtin.AtomicRmwOp"), .calling_convention_type => return out_stream.writeAll("std.builtin.CallingConvention"), @@ -821,11 +829,13 @@ pub const Value = extern union { .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int), .anyframe_type => Type.initTag(.@"anyframe"), .const_slice_u8_type => Type.initTag(.const_slice_u8), + .const_slice_u8_sentinel_0_type => Type.initTag(.const_slice_u8_sentinel_0), .anyerror_void_error_union_type => Type.initTag(.anyerror_void_error_union), .generic_poison_type => Type.initTag(.generic_poison), .enum_literal_type => Type.initTag(.enum_literal), .manyptr_u8_type => Type.initTag(.manyptr_u8), .manyptr_const_u8_type => Type.initTag(.manyptr_const_u8), + .manyptr_const_u8_sentinel_0_type => Type.initTag(.manyptr_const_u8_sentinel_0), .atomic_order_type => Type.initTag(.atomic_order), .atomic_rmw_op_type => Type.initTag(.atomic_rmw_op), .calling_convention_type => Type.initTag(.calling_convention), diff --git a/test/behavior.zig b/test/behavior.zig index 4a28e1b07a..2acd01fab1 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -75,6 +75,7 @@ test { _ = @import("behavior/bugs/3112.zig"); _ = @import("behavior/bugs/7250.zig"); _ = @import("behavior/cast_llvm.zig"); + _ = @import("behavior/enum_llvm.zig"); _ = @import("behavior/eval.zig"); _ = @import("behavior/floatop.zig"); _ = @import("behavior/fn.zig"); diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig index 38a92de164..6de1763deb 100644 --- a/test/behavior/enum.zig +++ b/test/behavior/enum.zig @@ -699,3 +699,126 @@ test "single field non-exhaustive enum" { try S.doTheTest(23); comptime try S.doTheTest(23); } + +const EnumWithTagValues = enum(u4) { + A = 1 << 0, + B = 1 << 1, + C = 1 << 2, + D = 1 << 3, +}; +test "enum with tag values don't require parens" { + try expect(@enumToInt(EnumWithTagValues.C) == 0b0100); +} + +const MultipleChoice2 = enum(u32) { + Unspecified1, + A = 20, + Unspecified2, + B = 40, + Unspecified3, + C = 60, + Unspecified4, + D = 1000, + Unspecified5, +}; + +test "cast integer literal to enum" { + try expect(@intToEnum(MultipleChoice2, 0) == MultipleChoice2.Unspecified1); + try expect(@intToEnum(MultipleChoice2, 40) == MultipleChoice2.B); +} + +test "enum with specified and unspecified tag values" { + try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2.D); + comptime try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2.D); +} + +fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) !void { + try expect(@enumToInt(x) == 1000); + try expect(1234 == switch (x) { + MultipleChoice2.A => 1, + MultipleChoice2.B => 2, + MultipleChoice2.C => 3, + MultipleChoice2.D => @as(u32, 1234), + MultipleChoice2.Unspecified1 => 5, + MultipleChoice2.Unspecified2 => 6, + MultipleChoice2.Unspecified3 => 7, + MultipleChoice2.Unspecified4 => 8, + MultipleChoice2.Unspecified5 => 9, + }); +} + +const Small2 = enum(u2) { One, Two }; +const Small = enum(u2) { One, Two, Three, Four }; + +test "set enum tag type" { + { + var x = Small.One; + x = Small.Two; + comptime try expect(Tag(Small) == u2); + } + { + var x = Small2.One; + x = Small2.Two; + comptime try expect(Tag(Small2) == u2); + } +} + +test "casting enum to its tag type" { + try testCastEnumTag(Small2.Two); + comptime try testCastEnumTag(Small2.Two); +} + +fn testCastEnumTag(value: Small2) !void { + try expect(@enumToInt(value) == 1); +} + +test "enum with 1 field but explicit tag type should still have the tag type" { + const Enum = enum(u8) { + B = 2, + }; + comptime try expect(@sizeOf(Enum) == @sizeOf(u8)); +} + +test "signed integer as enum tag" { + const SignedEnum = enum(i2) { + A0 = -1, + A1 = 0, + A2 = 1, + }; + + try expect(@enumToInt(SignedEnum.A0) == -1); + try expect(@enumToInt(SignedEnum.A1) == 0); + try expect(@enumToInt(SignedEnum.A2) == 1); +} + +test "enum with one member and custom tag type" { + const E = enum(u2) { + One, + }; + try expect(@enumToInt(E.One) == 0); + const E2 = enum(u2) { + One = 2, + }; + try expect(@enumToInt(E2.One) == 2); +} + +test "enum with one member and u1 tag type @enumToInt" { + const Enum = enum(u1) { + Test, + }; + try expect(@enumToInt(Enum.Test) == 0); +} + +test "enum with comptime_int tag type" { + const Enum = enum(comptime_int) { + One = 3, + Two = 2, + Three = 1, + }; + comptime try expect(Tag(Enum) == comptime_int); +} + +test "enum with one member default to u0 tag type" { + const E0 = enum { X }; + comptime try expect(Tag(E0) == u0); +} diff --git a/test/behavior/enum_llvm.zig b/test/behavior/enum_llvm.zig new file mode 100644 index 0000000000..bd93f48db7 --- /dev/null +++ b/test/behavior/enum_llvm.zig @@ -0,0 +1,49 @@ +const std = @import("std"); +const expect = std.testing.expect; +const mem = std.mem; +const Tag = std.meta.Tag; + +test "@tagName" { + try expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); + comptime try expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); +} + +fn testEnumTagNameBare(n: anytype) []const u8 { + return @tagName(n); +} + +const BareNumber = enum { One, Two, Three }; + +test "@tagName non-exhaustive enum" { + try expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B")); + comptime try expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B")); +} +const NonExhaustive = enum(u8) { A, B, _ }; + +test "@tagName is null-terminated" { + const S = struct { + fn doTheTest(n: BareNumber) !void { + try expect(@tagName(n)[3] == 0); + } + }; + try S.doTheTest(.Two); + try comptime S.doTheTest(.Two); +} + +test "tag name with assigned enum values" { + const LocalFoo = enum(u8) { + A = 1, + B = 0, + }; + var b = LocalFoo.B; + try expect(mem.eql(u8, @tagName(b), "B")); +} + +const Bar = enum { A, B, C, D }; + +test "enum literal casting to optional" { + var bar: ?Bar = undefined; + bar = .B; + + try expect(bar.? == Bar.B); +} diff --git a/test/behavior/enum_stage1.zig b/test/behavior/enum_stage1.zig index ba0fcb58bc..791f636633 100644 --- a/test/behavior/enum_stage1.zig +++ b/test/behavior/enum_stage1.zig @@ -2,47 +2,7 @@ const expect = @import("std").testing.expect; const mem = @import("std").mem; const Tag = @import("std").meta.Tag; -test "@tagName" { - try expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); - comptime try expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); -} - -test "@tagName non-exhaustive enum" { - try expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B")); - comptime try expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B")); -} - -test "@tagName is null-terminated" { - const S = struct { - fn doTheTest(n: BareNumber) !void { - try expect(@tagName(n)[3] == 0); - } - }; - try S.doTheTest(.Two); - try comptime S.doTheTest(.Two); -} - -fn testEnumTagNameBare(n: anytype) []const u8 { - return @tagName(n); -} - -const BareNumber = enum { One, Two, Three }; -const NonExhaustive = enum(u8) { A, B, _ }; const Small2 = enum(u2) { One, Two }; -const Small = enum(u2) { One, Two, Three, Four }; - -test "set enum tag type" { - { - var x = Small.One; - x = Small.Two; - comptime try expect(Tag(Small) == u2); - } - { - var x = Small2.One; - x = Small2.Two; - comptime try expect(Tag(Small2) == u2); - } -} const A = enum(u3) { One, Two, Three, Four, One2, Two2, Three2, Four2 }; const B = enum(u3) { One3, Two3, Three3, Four3, One23, Two23, Three23, Four23 }; @@ -87,15 +47,6 @@ fn getC(data: *const BitFieldOfEnums) C { return data.c; } -test "casting enum to its tag type" { - try testCastEnumTag(Small2.Two); - comptime try testCastEnumTag(Small2.Two); -} - -fn testCastEnumTag(value: Small2) !void { - try expect(@enumToInt(value) == 1); -} - const MultipleChoice2 = enum(u32) { Unspecified1, A = 20, @@ -108,31 +59,6 @@ const MultipleChoice2 = enum(u32) { Unspecified5, }; -test "enum with specified and unspecified tag values" { - try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2.D); - comptime try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2.D); -} - -fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) !void { - try expect(@enumToInt(x) == 1000); - try expect(1234 == switch (x) { - MultipleChoice2.A => 1, - MultipleChoice2.B => 2, - MultipleChoice2.C => 3, - MultipleChoice2.D => @as(u32, 1234), - MultipleChoice2.Unspecified1 => 5, - MultipleChoice2.Unspecified2 => 6, - MultipleChoice2.Unspecified3 => 7, - MultipleChoice2.Unspecified4 => 8, - MultipleChoice2.Unspecified5 => 9, - }); -} - -test "cast integer literal to enum" { - try expect(@intToEnum(MultipleChoice2, 0) == MultipleChoice2.Unspecified1); - try expect(@intToEnum(MultipleChoice2, 40) == MultipleChoice2.B); -} - const EnumWithOneMember = enum { Eof }; fn doALoopThing(id: EnumWithOneMember) void { @@ -157,32 +83,6 @@ test "switch on enum with one member is comptime known" { @compileError("analysis should not reach here"); } -const EnumWithTagValues = enum(u4) { - A = 1 << 0, - B = 1 << 1, - C = 1 << 2, - D = 1 << 3, -}; -test "enum with tag values don't require parens" { - try expect(@enumToInt(EnumWithTagValues.C) == 0b0100); -} - -test "enum with 1 field but explicit tag type should still have the tag type" { - const Enum = enum(u8) { - B = 2, - }; - comptime try expect(@sizeOf(Enum) == @sizeOf(u8)); -} - -test "tag name with assigned enum values" { - const LocalFoo = enum(u8) { - A = 1, - B = 0, - }; - var b = LocalFoo.B; - try expect(mem.eql(u8, @tagName(b), "B")); -} - test "enum literal in array literal" { const Items = enum { one, two }; const array = [_]Items{ .one, .two }; @@ -191,18 +91,6 @@ test "enum literal in array literal" { try expect(array[1] == .two); } -test "signed integer as enum tag" { - const SignedEnum = enum(i2) { - A0 = -1, - A1 = 0, - A2 = 1, - }; - - try expect(@enumToInt(SignedEnum.A0) == -1); - try expect(@enumToInt(SignedEnum.A1) == 0); - try expect(@enumToInt(SignedEnum.A2) == 1); -} - test "enum value allocation" { const LargeEnum = enum(u32) { A0 = 0x80000000, @@ -235,26 +123,8 @@ test "enum literal casting to tagged union" { } } -test "enum with one member and custom tag type" { - const E = enum(u2) { - One, - }; - try expect(@enumToInt(E.One) == 0); - const E2 = enum(u2) { - One = 2, - }; - try expect(@enumToInt(E2.One) == 2); -} - const Bar = enum { A, B, C, D }; -test "enum literal casting to optional" { - var bar: ?Bar = undefined; - bar = .B; - - try expect(bar.? == Bar.B); -} - test "enum literal casting to error union with payload enum" { var bar: error{B}!Bar = undefined; bar = .B; // should never cast to the error set @@ -262,27 +132,6 @@ test "enum literal casting to error union with payload enum" { try expect((try bar) == Bar.B); } -test "enum with one member and u1 tag type @enumToInt" { - const Enum = enum(u1) { - Test, - }; - try expect(@enumToInt(Enum.Test) == 0); -} - -test "enum with comptime_int tag type" { - const Enum = enum(comptime_int) { - One = 3, - Two = 2, - Three = 1, - }; - comptime try expect(Tag(Enum) == comptime_int); -} - -test "enum with one member default to u0 tag type" { - const E0 = enum { X }; - comptime try expect(Tag(E0) == u0); -} - test "tagName on enum literals" { try expect(mem.eql(u8, @tagName(.FooBar), "FooBar")); comptime try expect(mem.eql(u8, @tagName(.FooBar), "FooBar"));