mirror of
https://github.com/ziglang/zig.git
synced 2024-11-15 08:33:06 +00:00
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.
This commit is contained in:
parent
f41b9cdb6d
commit
c8fb36b36c
@ -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()) {
|
||||
|
@ -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 });
|
||||
|
51
src/Sema.zig
51
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,
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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",
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -155,6 +155,7 @@ const Writer = struct {
|
||||
.bool_to_int,
|
||||
.ret,
|
||||
.ret_load,
|
||||
.tag_name,
|
||||
=> try w.writeUnOp(s, inst),
|
||||
|
||||
.breakpoint,
|
||||
|
155
src/type.zig
155
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(
|
||||
|
@ -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),
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
}
|
||||
|
49
test/behavior/enum_llvm.zig
Normal file
49
test/behavior/enum_llvm.zig
Normal file
@ -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);
|
||||
}
|
@ -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"));
|
||||
|
Loading…
Reference in New Issue
Block a user