Merge pull request #12686 from Vexu/stage2-fixes

Stage2 fixes
This commit is contained in:
Andrew Kelley 2022-08-30 15:55:05 -04:00 committed by GitHub
commit f559ea95b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 528 additions and 201 deletions

View File

@ -226,6 +226,8 @@ pub const ResultLoc = union(enum) {
ref,
/// The expression will be coerced into this type, but it will be evaluated as an rvalue.
ty: Zir.Inst.Ref,
/// Same as `ty` but for shift operands.
ty_shift_operand: Zir.Inst.Ref,
/// Same as `ty` but it is guaranteed that Sema will additionally perform the coercion,
/// so no `as` instruction needs to be emitted.
coerced_ty: Zir.Inst.Ref,
@ -259,7 +261,7 @@ pub const ResultLoc = union(enum) {
fn strategy(rl: ResultLoc, block_scope: *GenZir) Strategy {
switch (rl) {
// In this branch there will not be any store_to_block_ptr instructions.
.none, .ty, .coerced_ty, .ref => return .{
.none, .ty, .ty_shift_operand, .coerced_ty, .ref => return .{
.tag = .break_operand,
.elide_store_to_block_ptr_instructions = false,
},
@ -302,6 +304,14 @@ pub const ResultLoc = union(enum) {
else => rl,
};
}
fn zirTag(rl: ResultLoc) Zir.Inst.Tag {
return switch (rl) {
.ty => .as_node,
.ty_shift_operand => .as_shift_operand,
else => unreachable,
};
}
};
pub const align_rl: ResultLoc = .{ .ty = .u29_type };
@ -1385,7 +1395,7 @@ fn arrayInitExpr(
const tag: Zir.Inst.Tag = if (types.array != .none) .array_init else .array_init_anon;
return arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag);
},
.ty, .coerced_ty => {
.ty, .ty_shift_operand, .coerced_ty => {
const tag: Zir.Inst.Tag = if (types.array != .none) .array_init else .array_init_anon;
const result = try arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag);
return rvalue(gz, rl, result, node);
@ -1631,7 +1641,7 @@ fn structInitExpr(
return structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon);
}
},
.ty, .coerced_ty => |ty_inst| {
.ty, .ty_shift_operand, .coerced_ty => |ty_inst| {
if (struct_init.ast.type_expr == 0) {
const result = try structInitExprRlNone(gz, scope, node, struct_init, ty_inst, .struct_init_anon);
return rvalue(gz, rl, result, node);
@ -2327,6 +2337,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
.anyframe_type,
.as,
.as_node,
.as_shift_operand,
.bit_and,
.bitcast,
.bit_or,
@ -2497,7 +2508,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
.field_parent_ptr,
.maximum,
.minimum,
.builtin_async_call,
.c_import,
.@"resume",
.@"await",
@ -7278,7 +7288,7 @@ fn as(
) InnerError!Zir.Inst.Ref {
const dest_type = try typeExpr(gz, scope, lhs);
switch (rl) {
.none, .discard, .ref, .ty, .coerced_ty => {
.none, .discard, .ref, .ty, .ty_shift_operand, .coerced_ty => {
const result = try reachableExpr(gz, scope, .{ .ty = dest_type }, rhs, node);
return rvalue(gz, rl, result, node);
},
@ -7959,7 +7969,8 @@ fn builtinCall(
return rvalue(gz, rl, result, node);
},
.async_call => {
const result = try gz.addPlNode(.builtin_async_call, node, Zir.Inst.AsyncCall{
const result = try gz.addExtendedPayload(.builtin_async_call, Zir.Inst.AsyncCall{
.node = gz.nodeIndexToRelative(node),
.frame_buffer = try expr(gz, scope, .none, params[0]),
.result_ptr = try expr(gz, scope, .none, params[1]),
.fn_ptr = try expr(gz, scope, .none, params[2]),
@ -8178,7 +8189,7 @@ fn shiftOp(
) InnerError!Zir.Inst.Ref {
const lhs = try expr(gz, scope, .none, lhs_node);
const log2_int_type = try gz.addUnNode(.typeof_log2_int_type, lhs, lhs_node);
const rhs = try expr(gz, scope, .{ .ty = log2_int_type }, rhs_node);
const rhs = try expr(gz, scope, .{ .ty_shift_operand = log2_int_type }, rhs_node);
const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
.lhs = lhs,
.rhs = rhs,
@ -9409,7 +9420,7 @@ fn rvalue(
}
return indexToRef(gop.value_ptr.*);
},
.ty => |ty_inst| {
.ty, .ty_shift_operand => |ty_inst| {
// Quickly eliminate some common, unnecessary type coercion.
const as_ty = @as(u64, @enumToInt(Zir.Inst.Ref.type_type)) << 32;
const as_comptime_int = @as(u64, @enumToInt(Zir.Inst.Ref.comptime_int_type)) << 32;
@ -9470,7 +9481,7 @@ fn rvalue(
=> return result, // type of result is already correct
// Need an explicit type coercion instruction.
else => return gz.addPlNode(.as_node, src_node, Zir.Inst.As{
else => return gz.addPlNode(rl.zirTag(), src_node, Zir.Inst.As{
.dest_type = ty_inst,
.operand = result,
}),
@ -10350,7 +10361,7 @@ const GenZir = struct {
// we emit ZIR for the block break instructions to have the result values,
// and then rvalue() on that to pass the value to the result location.
switch (parent_rl) {
.ty, .coerced_ty => |ty_inst| {
.ty, .ty_shift_operand, .coerced_ty => |ty_inst| {
gz.rl_ty_inst = ty_inst;
gz.break_result_loc = parent_rl;
},
@ -11506,7 +11517,7 @@ const GenZir = struct {
fn addRet(gz: *GenZir, rl: ResultLoc, operand: Zir.Inst.Ref, node: Ast.Node.Index) !void {
switch (rl) {
.ptr => |ret_ptr| _ = try gz.addUnNode(.ret_load, ret_ptr, node),
.ty => _ = try gz.addUnNode(.ret_node, operand, node),
.ty, .ty_shift_operand => _ = try gz.addUnNode(.ret_node, operand, node),
else => unreachable,
}
}

View File

@ -1888,7 +1888,7 @@ fn walkInstruction(
.expr = .{ .typeInfo = operand_index },
};
},
.as_node => {
.as_node, .as_shift_operand => {
const pl_node = data[inst_index].pl_node;
const extra = file.zir.extraData(Zir.Inst.As, pl_node.payload_index);
const dest_type_walk = try self.walkRef(

View File

@ -4766,6 +4766,24 @@ pub fn dump_argv(argv: []const []const u8) void {
std.debug.print("{s}\n", .{argv[argv.len - 1]});
}
pub fn getZigBackend(comp: Compilation) std.builtin.CompilerBackend {
const use_stage1 = build_options.have_stage1 and comp.bin_file.options.use_stage1;
if (use_stage1) return .stage1;
if (build_options.have_llvm and comp.bin_file.options.use_llvm) return .stage2_llvm;
const target = comp.bin_file.options.target;
if (target.ofmt == .c) return .stage2_c;
return switch (target.cpu.arch) {
.wasm32, .wasm64 => std.builtin.CompilerBackend.stage2_wasm,
.arm, .armeb, .thumb, .thumbeb => .stage2_arm,
.x86_64 => .stage2_x86_64,
.i386 => .stage2_x86,
.aarch64, .aarch64_be, .aarch64_32 => .stage2_aarch64,
.riscv64 => .stage2_riscv64,
.sparc64 => .stage2_sparc64,
else => .other,
};
}
pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Allocator.Error![:0]u8 {
const tracy_trace = trace(@src());
defer tracy_trace.end();
@ -4775,23 +4793,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca
const target = comp.getTarget();
const generic_arch_name = target.cpu.arch.genericName();
const use_stage1 = build_options.have_stage1 and comp.bin_file.options.use_stage1;
const zig_backend: std.builtin.CompilerBackend = blk: {
if (use_stage1) break :blk .stage1;
if (build_options.have_llvm and comp.bin_file.options.use_llvm) break :blk .stage2_llvm;
if (target.ofmt == .c) break :blk .stage2_c;
break :blk switch (target.cpu.arch) {
.wasm32, .wasm64 => std.builtin.CompilerBackend.stage2_wasm,
.arm, .armeb, .thumb, .thumbeb => .stage2_arm,
.x86_64 => .stage2_x86_64,
.i386 => .stage2_x86,
.aarch64, .aarch64_be, .aarch64_32 => .stage2_aarch64,
.riscv64 => .stage2_riscv64,
.sparc64 => .stage2_sparc64,
else => .other,
};
};
const zig_backend = comp.getZigBackend();
@setEvalBranchQuota(4000);
try buffer.writer().print(

View File

@ -712,6 +712,7 @@ fn analyzeBodyInner(
.vector_type => try sema.zirVectorType(block, inst),
.as => try sema.zirAs(block, inst),
.as_node => try sema.zirAsNode(block, inst),
.as_shift_operand => try sema.zirAsShiftOperand(block, inst),
.bit_and => try sema.zirBitwise(block, inst, .bit_and),
.bit_not => try sema.zirBitNot(block, inst),
.bit_or => try sema.zirBitwise(block, inst, .bit_or),
@ -848,7 +849,6 @@ fn analyzeBodyInner(
.mul_add => try sema.zirMulAdd(block, inst),
.builtin_call => try sema.zirBuiltinCall(block, inst),
.field_parent_ptr => try sema.zirFieldParentPtr(block, inst),
.builtin_async_call => try sema.zirBuiltinAsyncCall(block, inst),
.@"resume" => try sema.zirResume(block, inst),
.@"await" => try sema.zirAwait(block, inst),
.array_base_ptr => try sema.zirArrayBasePtr(block, inst),
@ -956,6 +956,7 @@ fn analyzeBodyInner(
.error_to_int => try sema.zirErrorToInt( block, extended),
.int_to_error => try sema.zirIntToError( block, extended),
.reify => try sema.zirReify( block, extended, inst),
.builtin_async_call => try sema.zirBuiltinAsyncCall( block, extended),
// zig fmt: on
.fence => {
try sema.zirFence(block, extended);
@ -6152,9 +6153,30 @@ fn analyzeCall(
if (ensure_result_used) {
try sema.ensureResultUsed(block, result, call_src);
}
if (call_tag == .call_always_tail) {
return sema.handleTailCall(block, call_src, func_ty, result);
}
return result;
}
fn handleTailCall(sema: *Sema, block: *Block, call_src: LazySrcLoc, func_ty: Type, result: Air.Inst.Ref) !Air.Inst.Ref {
const target = sema.mod.getTarget();
const backend = sema.mod.comp.getZigBackend();
if (!target_util.supportsTailCall(target, backend)) {
return sema.fail(block, call_src, "unable to perform tail call: compiler backend '{s}' does not support tail calls on target architecture '{s}' with the selected CPU feature flags", .{
@tagName(backend), @tagName(target.cpu.arch),
});
}
const func_decl = sema.mod.declPtr(sema.owner_func.?.owner_decl);
if (!func_ty.eql(func_decl.ty, sema.mod)) {
return sema.fail(block, call_src, "unable to perform tail call: type of function being called '{}' does not match type of calling function '{}'", .{
func_ty.fmt(sema.mod), func_decl.ty.fmt(sema.mod),
});
}
_ = try block.addUnOp(.ret, result);
return Air.Inst.Ref.unreachable_value;
}
fn analyzeInlineCallArg(
sema: *Sema,
arg_block: *Block,
@ -6670,7 +6692,8 @@ fn instantiateGenericCall(
try sema.requireFunctionBlock(block, call_src);
const comptime_args = callee.comptime_args.?;
const new_fn_info = mod.declPtr(callee.owner_decl).ty.fnInfo();
const func_ty = mod.declPtr(callee.owner_decl).ty;
const new_fn_info = func_ty.fnInfo();
const runtime_args_len = @intCast(u32, new_fn_info.param_types.len);
const runtime_args = try sema.arena.alloc(Air.Inst.Ref, runtime_args_len);
{
@ -6717,7 +6740,7 @@ fn instantiateGenericCall(
try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len +
runtime_args_len);
const func_inst = try block.addInst(.{
const result = try block.addInst(.{
.tag = call_tag,
.data = .{ .pl_op = .{
.operand = callee_inst,
@ -6729,9 +6752,12 @@ fn instantiateGenericCall(
sema.appendRefsAssumeCapacity(runtime_args);
if (ensure_result_used) {
try sema.ensureResultUsed(block, func_inst, call_src);
try sema.ensureResultUsed(block, result, call_src);
}
return func_inst;
if (call_tag == .call_always_tail) {
return sema.handleTailCall(block, call_src, func_ty, result);
}
return result;
}
fn emitDbgInline(
@ -8177,8 +8203,22 @@ fn zirParam(
.is_comptime = comptime_syntax,
.name = param_name,
});
const result = try sema.addConstant(param_ty, Value.initTag(.generic_poison));
try sema.inst_map.putNoClobber(sema.gpa, inst, result);
if (is_comptime) {
// If this is a comptime parameter we can add a constant generic_poison
// since this is also a generic parameter.
const result = try sema.addConstant(param_ty, Value.initTag(.generic_poison));
try sema.inst_map.putNoClobber(sema.gpa, inst, result);
} else {
// Otherwise we need a dummy runtime instruction.
const result_index = @intCast(Air.Inst.Index, sema.air_instructions.len);
try sema.air_instructions.append(sema.gpa, .{
.tag = .alloc,
.data = .{ .ty = param_ty },
});
const result = Air.indexToRef(result_index);
try sema.inst_map.putNoClobber(sema.gpa, inst, result);
}
}
fn zirParamAnytype(
@ -8225,7 +8265,7 @@ fn zirAs(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst
defer tracy.end();
const bin_inst = sema.code.instructions.items(.data)[inst].bin;
return sema.analyzeAs(block, sema.src, bin_inst.lhs, bin_inst.rhs);
return sema.analyzeAs(block, sema.src, bin_inst.lhs, bin_inst.rhs, false);
}
fn zirAsNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@ -8235,7 +8275,17 @@ fn zirAsNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const src = inst_data.src();
const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data;
return sema.analyzeAs(block, src, extra.dest_type, extra.operand);
return sema.analyzeAs(block, src, extra.dest_type, extra.operand, false);
}
fn zirAsShiftOperand(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const src = inst_data.src();
const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data;
return sema.analyzeAs(block, src, extra.dest_type, extra.operand, true);
}
fn analyzeAs(
@ -8244,6 +8294,7 @@ fn analyzeAs(
src: LazySrcLoc,
zir_dest_type: Zir.Inst.Ref,
zir_operand: Zir.Inst.Ref,
no_cast_to_comptime_int: bool,
) CompileError!Air.Inst.Ref {
const is_ret = if (Zir.refToIndex(zir_dest_type)) |ptr_index|
sema.code.instructions.items(.tag)[ptr_index] == .ret_type
@ -8255,7 +8306,7 @@ fn analyzeAs(
if (dest_ty.zigTypeTag() == .NoReturn) {
return sema.fail(block, src, "cannot cast to noreturn", .{});
}
return sema.coerceExtra(block, dest_ty, operand, src, true, is_ret) catch |err| switch (err) {
return sema.coerceExtra(block, dest_ty, operand, src, .{ .is_ret = is_ret, .no_cast_to_comptime_int = no_cast_to_comptime_int }) catch |err| switch (err) {
error.NotCoercible => unreachable,
else => |e| return e,
};
@ -10459,7 +10510,12 @@ fn zirShl(
const runtime_src = if (maybe_lhs_val) |lhs_val| rs: {
if (lhs_val.isUndef()) return sema.addConstUndef(lhs_ty);
const rhs_val = maybe_rhs_val orelse break :rs rhs_src;
const rhs_val = maybe_rhs_val orelse {
if (scalar_ty.zigTypeTag() == .ComptimeInt) {
return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be a comptime known", .{});
}
break :rs rhs_src;
};
const val = switch (air_tag) {
.shl_exact => val: {
@ -10583,7 +10639,10 @@ fn zirShr(
const target = sema.mod.getTarget();
const scalar_ty = lhs_ty.scalarType();
const runtime_src = if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| rs: {
const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs);
const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs);
const runtime_src = if (maybe_rhs_val) |rhs_val| rs: {
if (rhs_val.isUndef()) {
return sema.addConstUndef(lhs_ty);
}
@ -10615,7 +10674,7 @@ fn zirShr(
});
}
}
if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| {
if (maybe_lhs_val) |lhs_val| {
if (lhs_val.isUndef()) {
return sema.addConstUndef(lhs_ty);
}
@ -10633,6 +10692,10 @@ fn zirShr(
}
} else rhs_src;
if (maybe_rhs_val == null and scalar_ty.zigTypeTag() == .ComptimeInt) {
return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be a comptime known", .{});
}
try sema.requireRuntimeBlock(block, src, runtime_src);
const result = try block.addBinOp(air_tag, lhs, rhs);
if (block.wantSafety()) {
@ -15353,7 +15416,7 @@ fn analyzeRet(
if (sema.fn_ret_ty.zigTypeTag() == .ErrorUnion) {
try sema.addToInferredErrorSet(uncasted_operand);
}
const operand = sema.coerceExtra(block, sema.fn_ret_ty, uncasted_operand, src, true, true) catch |err| switch (err) {
const operand = sema.coerceExtra(block, sema.fn_ret_ty, uncasted_operand, src, .{ .is_ret = true }) catch |err| switch (err) {
error.NotCoercible => unreachable,
else => |e| return e,
};
@ -19262,7 +19325,7 @@ fn resolveCallOptions(
return wanted_modifier;
},
// These can be upgraded to comptime. nosuspend bit can be safely ignored.
.always_tail, .always_inline, .compile_time => {
.always_inline, .compile_time => {
_ = (try sema.resolveDefinedValue(block, func_src, func)) orelse {
return sema.fail(block, func_src, "modifier '{s}' requires a comptime-known function", .{@tagName(wanted_modifier)});
};
@ -19272,6 +19335,12 @@ fn resolveCallOptions(
}
return wanted_modifier;
},
.always_tail => {
if (is_comptime) {
return .compile_time;
}
return wanted_modifier;
},
.async_kw => {
if (is_nosuspend) {
return sema.fail(block, modifier_src, "modifier 'async_kw' cannot be used inside nosuspend block", .{});
@ -19614,9 +19683,9 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
});
}
fn zirBuiltinAsyncCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const src = inst_data.src();
fn zirBuiltinAsyncCall(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
const src = LazySrcLoc.nodeOffset(extra.node);
return sema.failWithUseOfAsync(block, src);
}
@ -21871,6 +21940,7 @@ fn unionFieldPtr(
.mutable = union_ptr_ty.ptrIsMutable(),
.@"addrspace" = union_ptr_ty.ptrAddressSpace(),
});
const enum_field_index = @intCast(u32, union_obj.tag_ty.enumFieldIndex(field_name).?);
if (initializing and field.ty.zigTypeTag() == .NoReturn) {
const msg = msg: {
@ -21892,11 +21962,10 @@ fn unionFieldPtr(
if (union_val.isUndef()) {
return sema.failWithUseOfUndef(block, src);
}
const enum_field_index = union_obj.tag_ty.enumFieldIndex(field_name).?;
const tag_and_val = union_val.castTag(.@"union").?.data;
var field_tag_buf: Value.Payload.U32 = .{
.base = .{ .tag = .enum_field_index },
.data = @intCast(u32, enum_field_index),
.data = enum_field_index,
};
const field_tag = Value.initPayload(&field_tag_buf.base);
const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, sema.mod);
@ -21928,7 +21997,7 @@ fn unionFieldPtr(
if (!initializing and union_obj.layout == .Auto and block.wantSafety() and
union_ty.unionTagTypeSafety() != null and union_obj.fields.count() > 1)
{
const wanted_tag_val = try Value.Tag.enum_field_index.create(sema.arena, field_index);
const wanted_tag_val = try Value.Tag.enum_field_index.create(sema.arena, enum_field_index);
const wanted_tag = try sema.addConstant(union_obj.tag_ty, wanted_tag_val);
// TODO would it be better if get_union_tag supported pointers to unions?
const union_val = try block.addTyOp(.load, union_ty, union_ptr);
@ -21958,15 +22027,15 @@ fn unionFieldVal(
const union_obj = union_ty.cast(Type.Payload.Union).?.data;
const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src);
const field = union_obj.fields.values()[field_index];
const enum_field_index = @intCast(u32, union_obj.tag_ty.enumFieldIndex(field_name).?);
if (try sema.resolveMaybeUndefVal(block, src, union_byval)) |union_val| {
if (union_val.isUndef()) return sema.addConstUndef(field.ty);
const tag_and_val = union_val.castTag(.@"union").?.data;
const enum_field_index = union_obj.tag_ty.enumFieldIndex(field_name).?;
var field_tag_buf: Value.Payload.U32 = .{
.base = .{ .tag = .enum_field_index },
.data = @intCast(u32, enum_field_index),
.data = enum_field_index,
};
const field_tag = Value.initPayload(&field_tag_buf.base);
const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, sema.mod);
@ -22002,7 +22071,7 @@ fn unionFieldVal(
if (union_obj.layout == .Auto and block.wantSafety() and
union_ty.unionTagTypeSafety() != null and union_obj.fields.count() > 1)
{
const wanted_tag_val = try Value.Tag.enum_field_index.create(sema.arena, field_index);
const wanted_tag_val = try Value.Tag.enum_field_index.create(sema.arena, enum_field_index);
const wanted_tag = try sema.addConstant(union_obj.tag_ty, wanted_tag_val);
const active_tag = try block.addTyOp(.get_union_tag, union_obj.tag_ty, union_byval);
const ok = try block.addBinOp(.cmp_eq, active_tag, wanted_tag);
@ -22504,7 +22573,7 @@ fn coerce(
inst: Air.Inst.Ref,
inst_src: LazySrcLoc,
) CompileError!Air.Inst.Ref {
return sema.coerceExtra(block, dest_ty_unresolved, inst, inst_src, true, false) catch |err| switch (err) {
return sema.coerceExtra(block, dest_ty_unresolved, inst, inst_src, .{}) catch |err| switch (err) {
error.NotCoercible => unreachable,
else => |e| return e,
};
@ -22516,14 +22585,22 @@ const CoersionError = CompileError || error{
NotCoercible,
};
const CoerceOpts = struct {
/// Should coerceExtra emit error messages.
report_err: bool = true,
/// Ignored if `report_err == false`.
is_ret: bool = false,
/// Should coercion to comptime_int ermit an error message.
no_cast_to_comptime_int: bool = false,
};
fn coerceExtra(
sema: *Sema,
block: *Block,
dest_ty_unresolved: Type,
inst: Air.Inst.Ref,
inst_src: LazySrcLoc,
report_err: bool,
is_ret: bool,
opts: CoerceOpts,
) CoersionError!Air.Inst.Ref {
switch (dest_ty_unresolved.tag()) {
.var_args_param => return sema.coerceVarArgParam(block, inst, inst_src),
@ -22575,7 +22652,7 @@ fn coerceExtra(
// T to ?T
const child_type = try dest_ty.optionalChildAlloc(sema.arena);
const intermediate = sema.coerceExtra(block, child_type, inst, inst_src, false, is_ret) catch |err| switch (err) {
const intermediate = sema.coerceExtra(block, child_type, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) {
error.NotCoercible => {
if (in_memory_result == .no_match) {
// Try to give more useful notes
@ -22691,7 +22768,7 @@ fn coerceExtra(
return sema.addConstant(dest_ty, Value.@"null");
},
.ComptimeInt => {
const addr = sema.coerceExtra(block, Type.usize, inst, inst_src, false, is_ret) catch |err| switch (err) {
const addr = sema.coerceExtra(block, Type.usize, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) {
error.NotCoercible => break :pointer,
else => |e| return e,
};
@ -22702,7 +22779,7 @@ fn coerceExtra(
.signed => Type.isize,
.unsigned => Type.usize,
};
const addr = sema.coerceExtra(block, ptr_size_ty, inst, inst_src, false, is_ret) catch |err| switch (err) {
const addr = sema.coerceExtra(block, ptr_size_ty, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) {
error.NotCoercible => {
// Try to give more useful notes
in_memory_result = try sema.coerceInMemoryAllowed(block, ptr_size_ty, inst_ty, false, target, dest_ty_src, inst_src);
@ -22828,7 +22905,13 @@ fn coerceExtra(
},
.Int, .ComptimeInt => switch (inst_ty.zigTypeTag()) {
.Float, .ComptimeFloat => float: {
const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse break :float;
const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse {
if (dest_ty.zigTypeTag() == .ComptimeInt) {
if (!opts.report_err) return error.NotCoercible;
return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_int' must be comptime known");
}
break :float;
};
if (val.floatHasFraction()) {
return sema.fail(
@ -22845,11 +22928,16 @@ fn coerceExtra(
if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| {
// comptime known integer to other number
if (!(try sema.intFitsInType(block, inst_src, val, dest_ty, null))) {
if (!report_err) return error.NotCoercible;
if (!opts.report_err) return error.NotCoercible;
return sema.fail(block, inst_src, "type '{}' cannot represent integer value '{}'", .{ dest_ty.fmt(sema.mod), val.fmtValue(inst_ty, sema.mod) });
}
return try sema.addConstant(dest_ty, val);
}
if (dest_ty.zigTypeTag() == .ComptimeInt) {
if (!opts.report_err) return error.NotCoercible;
if (opts.no_cast_to_comptime_int) return inst;
return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_int' must be comptime known");
}
// integer widening
const dst_info = dest_ty.intInfo(target);
@ -22886,6 +22974,7 @@ fn coerceExtra(
}
return try sema.addConstant(dest_ty, result_val);
} else if (dest_ty.zigTypeTag() == .ComptimeFloat) {
if (!opts.report_err) return error.NotCoercible;
return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_float' must be comptime known");
}
@ -22898,7 +22987,13 @@ fn coerceExtra(
}
},
.Int, .ComptimeInt => int: {
const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse break :int;
const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse {
if (dest_ty.zigTypeTag() == .ComptimeFloat) {
if (!opts.report_err) return error.NotCoercible;
return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_float' must be comptime known");
}
break :int;
};
const result_val = try val.intToFloat(sema.arena, inst_ty, dest_ty, target);
// TODO implement this compile error
//const int_again_val = try result_val.floatToInt(sema.arena, inst_ty);
@ -23050,9 +23145,9 @@ fn coerceExtra(
return sema.addConstUndef(dest_ty);
}
if (!report_err) return error.NotCoercible;
if (!opts.report_err) return error.NotCoercible;
if (is_ret and dest_ty.zigTypeTag() == .NoReturn) {
if (opts.is_ret and dest_ty.zigTypeTag() == .NoReturn) {
const msg = msg: {
const msg = try sema.errMsg(block, inst_src, "function declared 'noreturn' returns", .{});
errdefer msg.destroy(sema.gpa);
@ -23089,7 +23184,7 @@ fn coerceExtra(
try in_memory_result.report(sema, block, inst_src, msg);
// Add notes about function return type
if (is_ret and sema.mod.test_functions.get(sema.func.?.owner_decl) == null) {
if (opts.is_ret and sema.mod.test_functions.get(sema.func.?.owner_decl) == null) {
const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
const src_decl = sema.mod.declPtr(sema.func.?.owner_decl);
if (inst_ty.isError() and !dest_ty.isError()) {
@ -24050,7 +24145,7 @@ fn storePtr2(
// https://github.com/ziglang/zig/issues/11154
if (sema.obtainBitCastedVectorPtr(ptr)) |vector_ptr| {
const vector_ty = sema.typeOf(vector_ptr).childType();
const vector = sema.coerceExtra(block, vector_ty, uncasted_operand, operand_src, true, is_ret) catch |err| switch (err) {
const vector = sema.coerceExtra(block, vector_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) {
error.NotCoercible => unreachable,
else => |e| return e,
};
@ -24058,7 +24153,7 @@ fn storePtr2(
return;
}
const operand = sema.coerceExtra(block, elem_ty, uncasted_operand, operand_src, true, is_ret) catch |err| switch (err) {
const operand = sema.coerceExtra(block, elem_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) {
error.NotCoercible => unreachable,
else => |e| return e,
};
@ -26793,7 +26888,7 @@ fn wrapErrorUnionPayload(
inst_src: LazySrcLoc,
) !Air.Inst.Ref {
const dest_payload_ty = dest_ty.errorUnionPayload();
const coerced = try sema.coerceExtra(block, dest_payload_ty, inst, inst_src, false, false);
const coerced = try sema.coerceExtra(block, dest_payload_ty, inst, inst_src, .{ .report_err = false });
if (try sema.resolveMaybeUndefVal(block, inst_src, coerced)) |val| {
return sema.addConstant(dest_ty, try Value.Tag.eu_payload.create(sema.arena, val));
}

View File

@ -242,6 +242,8 @@ pub const Inst = struct {
/// Type coercion to the function's return type.
/// Uses the `pl_node` field. Payload is `As`. AST node could be many things.
as_node,
/// Same as `as_node` but ignores runtime to comptime int error.
as_shift_operand,
/// Bitwise AND. `&`
bit_and,
/// Reinterpret the memory representation of a value as a different type.
@ -942,9 +944,6 @@ pub const Inst = struct {
/// Implements the `@maximum` builtin.
/// Uses the `pl_node` union field with payload `Bin`
maximum,
/// Implements the `@asyncCall` builtin.
/// Uses the `pl_node` union field with payload `AsyncCall`.
builtin_async_call,
/// Implements the `@cImport` builtin.
/// Uses the `pl_node` union field with payload `Block`.
c_import,
@ -1029,6 +1028,7 @@ pub const Inst = struct {
.anyframe_type,
.as,
.as_node,
.as_shift_operand,
.bit_and,
.bitcast,
.bit_or,
@ -1231,7 +1231,6 @@ pub const Inst = struct {
.memcpy,
.memset,
.minimum,
.builtin_async_call,
.c_import,
.@"resume",
.@"await",
@ -1339,6 +1338,7 @@ pub const Inst = struct {
.anyframe_type,
.as,
.as_node,
.as_shift_operand,
.bit_and,
.bitcast,
.bit_or,
@ -1513,7 +1513,6 @@ pub const Inst = struct {
.field_parent_ptr,
.maximum,
.minimum,
.builtin_async_call,
.c_import,
.@"resume",
.@"await",
@ -1577,6 +1576,7 @@ pub const Inst = struct {
.anyframe_type = .un_node,
.as = .bin,
.as_node = .pl_node,
.as_shift_operand = .pl_node,
.bit_and = .pl_node,
.bitcast = .pl_node,
.bit_not = .un_node,
@ -1801,7 +1801,6 @@ pub const Inst = struct {
.memcpy = .pl_node,
.memset = .pl_node,
.minimum = .pl_node,
.builtin_async_call = .pl_node,
.c_import = .pl_node,
.alloc = .un_node,
@ -1972,6 +1971,9 @@ pub const Inst = struct {
/// `operand` is payload index to `UnNode`.
/// `small` contains `NameStrategy
reify,
/// Implements the `@asyncCall` builtin.
/// `operand` is payload index to `AsyncCall`.
builtin_async_call,
pub const InstData = struct {
opcode: Extended,
@ -3454,6 +3456,7 @@ pub const Inst = struct {
};
pub const AsyncCall = struct {
node: i32,
frame_buffer: Ref,
result_ptr: Ref,
fn_ptr: Ref,

View File

@ -177,6 +177,116 @@ pub fn targetTriple(allocator: Allocator, target: std.Target) ![:0]u8 {
return llvm_triple.toOwnedSliceSentinel(0);
}
pub fn targetOs(os_tag: std.Target.Os.Tag) llvm.OSType {
return switch (os_tag) {
.freestanding, .other, .opencl, .glsl450, .vulkan, .plan9 => .UnknownOS,
.windows, .uefi => .Win32,
.ananas => .Ananas,
.cloudabi => .CloudABI,
.dragonfly => .DragonFly,
.freebsd => .FreeBSD,
.fuchsia => .Fuchsia,
.ios => .IOS,
.kfreebsd => .KFreeBSD,
.linux => .Linux,
.lv2 => .Lv2,
.macos => .MacOSX,
.netbsd => .NetBSD,
.openbsd => .OpenBSD,
.solaris => .Solaris,
.zos => .ZOS,
.haiku => .Haiku,
.minix => .Minix,
.rtems => .RTEMS,
.nacl => .NaCl,
.aix => .AIX,
.cuda => .CUDA,
.nvcl => .NVCL,
.amdhsa => .AMDHSA,
.ps4 => .PS4,
.elfiamcu => .ELFIAMCU,
.tvos => .TvOS,
.watchos => .WatchOS,
.mesa3d => .Mesa3D,
.contiki => .Contiki,
.amdpal => .AMDPAL,
.hermit => .HermitCore,
.hurd => .Hurd,
.wasi => .WASI,
.emscripten => .Emscripten,
};
}
pub fn targetArch(arch_tag: std.Target.Cpu.Arch) llvm.ArchType {
return switch (arch_tag) {
.arm => .arm,
.armeb => .armeb,
.aarch64 => .aarch64,
.aarch64_be => .aarch64_be,
.aarch64_32 => .aarch64_32,
.arc => .arc,
.avr => .avr,
.bpfel => .bpfel,
.bpfeb => .bpfeb,
.csky => .csky,
.hexagon => .hexagon,
.m68k => .m68k,
.mips => .mips,
.mipsel => .mipsel,
.mips64 => .mips64,
.mips64el => .mips64el,
.msp430 => .msp430,
.powerpc => .ppc,
.powerpcle => .ppcle,
.powerpc64 => .ppc64,
.powerpc64le => .ppc64le,
.r600 => .r600,
.amdgcn => .amdgcn,
.riscv32 => .riscv32,
.riscv64 => .riscv64,
.sparc => .sparc,
.sparc64 => .sparcv9, // In LLVM, sparc64 == sparcv9.
.sparcel => .sparcel,
.s390x => .systemz,
.tce => .tce,
.tcele => .tcele,
.thumb => .thumb,
.thumbeb => .thumbeb,
.i386 => .x86,
.x86_64 => .x86_64,
.xcore => .xcore,
.nvptx => .nvptx,
.nvptx64 => .nvptx64,
.le32 => .le32,
.le64 => .le64,
.amdil => .amdil,
.amdil64 => .amdil64,
.hsail => .hsail,
.hsail64 => .hsail64,
.spir => .spir,
.spir64 => .spir64,
.kalimba => .kalimba,
.shave => .shave,
.lanai => .lanai,
.wasm32 => .wasm32,
.wasm64 => .wasm64,
.renderscript32 => .renderscript32,
.renderscript64 => .renderscript64,
.ve => .ve,
.spu_2, .spirv32, .spirv64 => .UnknownArch,
};
}
pub fn supportsTailCall(target: std.Target) bool {
switch (target.cpu.arch) {
.wasm32, .wasm64 => return std.Target.wasm.featureSetHas(target.cpu.features, .tail_call),
// Although these ISAs support tail calls, LLVM does not support tail calls on them.
.mips, .mipsel, .mips64, .mips64el => return false,
.powerpc, .powerpcle, .powerpc64, .powerpc64le => return false,
else => return true,
}
}
pub const Object = struct {
gpa: Allocator,
module: *Module,
@ -4522,7 +4632,7 @@ pub const FuncGen = struct {
"",
);
if (return_type.isNoReturn()) {
if (return_type.isNoReturn() and attr != .AlwaysTail) {
_ = self.builder.buildUnreachable();
return null;
}
@ -8527,12 +8637,26 @@ pub const FuncGen = struct {
const union_llvm_ty = try self.dg.lowerType(union_ty);
const target = self.dg.module.getTarget();
const layout = union_ty.unionGetLayout(target);
const union_obj = union_ty.cast(Type.Payload.Union).?.data;
const tag_int = blk: {
const tag_ty = union_ty.unionTagTypeHypothetical();
const union_field_name = union_obj.fields.keys()[extra.field_index];
const enum_field_index = tag_ty.enumFieldIndex(union_field_name).?;
var tag_val_payload: Value.Payload.U32 = .{
.base = .{ .tag = .enum_field_index },
.data = @intCast(u32, enum_field_index),
};
const tag_val = Value.initPayload(&tag_val_payload.base);
var int_payload: Value.Payload.U64 = undefined;
const tag_int_val = tag_val.enumToInt(tag_ty, &int_payload);
break :blk tag_int_val.toUnsignedInt(target);
};
if (layout.payload_size == 0) {
if (layout.tag_size == 0) {
return null;
}
assert(!isByRef(union_ty));
return union_llvm_ty.constInt(extra.field_index, .False);
return union_llvm_ty.constInt(tag_int, .False);
}
assert(isByRef(union_ty));
// The llvm type of the alloca will the the named LLVM union type, which will not
@ -8541,7 +8665,6 @@ pub const FuncGen = struct {
// then set the fields appropriately.
const result_ptr = self.buildAlloca(union_llvm_ty);
const llvm_payload = try self.resolveInst(extra.init);
const union_obj = union_ty.cast(Type.Payload.Union).?.data;
assert(union_obj.haveFieldTypes());
const field = union_obj.fields.values()[extra.field_index];
const field_llvm_ty = try self.dg.lowerType(field.ty);
@ -8625,7 +8748,7 @@ pub const FuncGen = struct {
};
const field_ptr = self.builder.buildInBoundsGEP(casted_ptr, &indices, indices.len, "");
const tag_llvm_ty = try self.dg.lowerType(union_obj.tag_ty);
const llvm_tag = tag_llvm_ty.constInt(extra.field_index, .False);
const llvm_tag = tag_llvm_ty.constInt(tag_int, .False);
const store_inst = self.builder.buildStore(llvm_tag, field_ptr);
store_inst.setAlignment(union_obj.tag_ty.abiAlignment(target));
}

View File

@ -931,9 +931,10 @@ pub const File = struct {
std.debug.print("\n", .{});
}
const llvm = @import("codegen/llvm/bindings.zig");
const os_type = @import("target.zig").osToLLVM(base.options.target.os.tag);
const bad = llvm.WriteArchive(full_out_path_z, object_files.items.ptr, object_files.items.len, os_type);
const llvm_bindings = @import("codegen/llvm/bindings.zig");
const llvm = @import("codegen/llvm.zig");
const os_tag = llvm.targetOs(base.options.target.os.tag);
const bad = llvm_bindings.WriteArchive(full_out_path_z, object_files.items.ptr, object_files.items.len, os_tag);
if (bad) return error.UnableToWriteArchive;
if (!base.options.disable_lld_caching) {

View File

@ -6,7 +6,6 @@ const assert = std.debug.assert;
const log = std.log.scoped(.mingw);
const builtin = @import("builtin");
const target_util = @import("target.zig");
const Compilation = @import("Compilation.zig");
const build_options = @import("build_options");
const Cache = @import("Cache.zig");
@ -404,11 +403,12 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
});
errdefer comp.gpa.free(lib_final_path);
const llvm = @import("codegen/llvm/bindings.zig");
const arch_type = target_util.archToLLVM(target.cpu.arch);
const llvm_bindings = @import("codegen/llvm/bindings.zig");
const llvm = @import("codegen/llvm.zig");
const arch_tag = llvm.targetArch(target.cpu.arch);
const def_final_path_z = try arena.dupeZ(u8, def_final_path);
const lib_final_path_z = try arena.dupeZ(u8, lib_final_path);
if (llvm.WriteImportLibrary(def_final_path_z.ptr, arch_type, lib_final_path_z.ptr, true)) {
if (llvm_bindings.WriteImportLibrary(def_final_path_z.ptr, arch_tag, lib_final_path_z.ptr, true)) {
// TODO surface a proper error here
log.err("unable to turn {s}.def into {s}.lib", .{ lib_name, lib_name });
return error.WritingImportLibFailed;

View File

@ -283,7 +283,6 @@ const Writer = struct {
.mul_add => try self.writeMulAdd(stream, inst),
.field_parent_ptr => try self.writeFieldParentPtr(stream, inst),
.builtin_call => try self.writeBuiltinCall(stream, inst),
.builtin_async_call => try self.writeBuiltinAsyncCall(stream, inst),
.struct_init_anon,
.struct_init_anon_ref,
@ -397,7 +396,7 @@ const Writer = struct {
.field_val_named,
=> try self.writePlNodeFieldNamed(stream, inst),
.as_node => try self.writeAs(stream, inst),
.as_node, .as_shift_operand => try self.writeAs(stream, inst),
.repeat,
.repeat_inline,
@ -531,6 +530,7 @@ const Writer = struct {
try stream.writeAll(") ");
try self.writeSrc(stream, src);
},
.builtin_async_call => try self.writeBuiltinAsyncCall(stream, extended),
}
}
@ -814,9 +814,8 @@ const Writer = struct {
try self.writeSrc(stream, inst_data.src());
}
fn writeBuiltinAsyncCall(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
const inst_data = self.code.instructions.items(.data)[inst].pl_node;
const extra = self.code.extraData(Zir.Inst.AsyncCall, inst_data.payload_index).data;
fn writeBuiltinAsyncCall(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void {
const extra = self.code.extraData(Zir.Inst.AsyncCall, extended.operand).data;
try self.writeInstRef(stream, extra.frame_buffer);
try stream.writeAll(", ");
try self.writeInstRef(stream, extra.result_ptr);
@ -825,7 +824,7 @@ const Writer = struct {
try stream.writeAll(", ");
try self.writeInstRef(stream, extra.args);
try stream.writeAll(") ");
try self.writeSrc(stream, inst_data.src());
try self.writeSrc(stream, LazySrcLoc.nodeOffset(extra.node));
}
fn writeParam(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {

View File

@ -1,5 +1,4 @@
const std = @import("std");
const llvm = @import("codegen/llvm/bindings.zig");
const Type = @import("type.zig").Type;
pub const ArchOsAbi = struct {
@ -317,106 +316,6 @@ pub fn supportsReturnAddress(target: std.Target) bool {
};
}
pub fn osToLLVM(os_tag: std.Target.Os.Tag) llvm.OSType {
return switch (os_tag) {
.freestanding, .other, .opencl, .glsl450, .vulkan, .plan9 => .UnknownOS,
.windows, .uefi => .Win32,
.ananas => .Ananas,
.cloudabi => .CloudABI,
.dragonfly => .DragonFly,
.freebsd => .FreeBSD,
.fuchsia => .Fuchsia,
.ios => .IOS,
.kfreebsd => .KFreeBSD,
.linux => .Linux,
.lv2 => .Lv2,
.macos => .MacOSX,
.netbsd => .NetBSD,
.openbsd => .OpenBSD,
.solaris => .Solaris,
.zos => .ZOS,
.haiku => .Haiku,
.minix => .Minix,
.rtems => .RTEMS,
.nacl => .NaCl,
.aix => .AIX,
.cuda => .CUDA,
.nvcl => .NVCL,
.amdhsa => .AMDHSA,
.ps4 => .PS4,
.elfiamcu => .ELFIAMCU,
.tvos => .TvOS,
.watchos => .WatchOS,
.mesa3d => .Mesa3D,
.contiki => .Contiki,
.amdpal => .AMDPAL,
.hermit => .HermitCore,
.hurd => .Hurd,
.wasi => .WASI,
.emscripten => .Emscripten,
};
}
pub fn archToLLVM(arch_tag: std.Target.Cpu.Arch) llvm.ArchType {
return switch (arch_tag) {
.arm => .arm,
.armeb => .armeb,
.aarch64 => .aarch64,
.aarch64_be => .aarch64_be,
.aarch64_32 => .aarch64_32,
.arc => .arc,
.avr => .avr,
.bpfel => .bpfel,
.bpfeb => .bpfeb,
.csky => .csky,
.hexagon => .hexagon,
.m68k => .m68k,
.mips => .mips,
.mipsel => .mipsel,
.mips64 => .mips64,
.mips64el => .mips64el,
.msp430 => .msp430,
.powerpc => .ppc,
.powerpcle => .ppcle,
.powerpc64 => .ppc64,
.powerpc64le => .ppc64le,
.r600 => .r600,
.amdgcn => .amdgcn,
.riscv32 => .riscv32,
.riscv64 => .riscv64,
.sparc => .sparc,
.sparc64 => .sparcv9, // In LLVM, sparc64 == sparcv9.
.sparcel => .sparcel,
.s390x => .systemz,
.tce => .tce,
.tcele => .tcele,
.thumb => .thumb,
.thumbeb => .thumbeb,
.i386 => .x86,
.x86_64 => .x86_64,
.xcore => .xcore,
.nvptx => .nvptx,
.nvptx64 => .nvptx64,
.le32 => .le32,
.le64 => .le64,
.amdil => .amdil,
.amdil64 => .amdil64,
.hsail => .hsail,
.hsail64 => .hsail64,
.spir => .spir,
.spir64 => .spir64,
.kalimba => .kalimba,
.shave => .shave,
.lanai => .lanai,
.wasm32 => .wasm32,
.wasm64 => .wasm64,
.renderscript32 => .renderscript32,
.renderscript64 => .renderscript64,
.ve => .ve,
.spu_2, .spirv32, .spirv64 => .UnknownArch,
};
}
fn eqlIgnoreCase(ignore_case: bool, a: []const u8, b: []const u8) bool {
if (ignore_case) {
return std.ascii.eqlIgnoreCase(a, b);
@ -770,3 +669,10 @@ pub fn supportsFunctionAlignment(target: std.Target) bool {
else => true,
};
}
pub fn supportsTailCall(target: std.Target, backend: std.builtin.CompilerBackend) bool {
switch (backend) {
.stage1, .stage2_llvm => return @import("codegen/llvm.zig").supportsTailCall(target),
else => return false,
}
}

View File

@ -261,3 +261,71 @@ test "arguments to comptime parameters generated in comptime blocks" {
};
S.foo(S.fortyTwo());
}
test "forced tail call" {
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_llvm) {
// Only attempt this test on targets we know have tail call support in LLVM.
if (builtin.cpu.arch != .x86_64 and builtin.cpu.arch != .aarch64) {
return error.SkipZigTest;
}
}
const S = struct {
fn fibonacciTailInternal(n: u16, a: u16, b: u16) u16 {
if (n == 0) return a;
if (n == 1) return b;
return @call(
.{ .modifier = .always_tail },
fibonacciTailInternal,
.{ n - 1, b, a + b },
);
}
fn fibonacciTail(n: u16) u16 {
return fibonacciTailInternal(n, 0, 1);
}
};
try expect(S.fibonacciTail(10) == 55);
}
test "inline call preserves tail call" {
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_llvm) {
// Only attempt this test on targets we know have tail call support in LLVM.
if (builtin.cpu.arch != .x86_64 and builtin.cpu.arch != .aarch64) {
return error.SkipZigTest;
}
}
const max = std.math.maxInt(u16);
const S = struct {
var a: u16 = 0;
fn foo() void {
return bar();
}
inline fn bar() void {
if (a == max) return;
// Stack overflow if not tail called
var buf: [max]u16 = undefined;
buf[a] = a;
a += 1;
return @call(.{ .modifier = .always_tail }, foo, .{});
}
};
S.foo();
try expect(S.a == std.math.maxInt(u16));
}

View File

@ -1324,3 +1324,31 @@ test "union and enum field order doesn't match" {
x = .b;
try expect(x == .b);
}
test "@unionInit uses tag value instead of field index" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
const E = enum(u8) {
b = 255,
a = 3,
};
const U = union(E) {
a: usize,
b: isize,
};
var i: isize = -1;
var u = @unionInit(U, "b", i);
{
var a = u.b;
try expect(a == i);
}
{
var a = &u.b;
try expect(a.* == i);
}
try expect(@enumToInt(u) == 255);
}

View File

@ -0,0 +1,12 @@
fn myFn(_: usize) void {
return;
}
pub export fn entry() void {
@call(.{ .modifier = .always_tail }, myFn, .{0});
}
// error
// backend=llvm
// target=native
//
// :5:5: error: unable to perform tail call: type of function being called 'fn(usize) void' does not match type of calling function 'fn() callconv(.C) void'

View File

@ -0,0 +1,16 @@
export fn entry() void {
const llamas1 = makeLlamas(5);
const llamas2 = makeLlamas(5);
_ = llamas1;
_ = llamas2;
}
fn makeLlamas(count: usize) [count]u8 {
_ = count;
}
// error
// target=native
//
// :8:30: error: unable to resolve comptime value
// :8:30: note: array length must be comptime known

View File

@ -0,0 +1,31 @@
pub export fn entry() void {
var a: u32 = 0;
_ = @as(comptime_int, a);
}
pub export fn entry2() void{
var a: u32 = 0;
_ = @as(comptime_float, a);
}
pub export fn entry3() void{
comptime var aa: comptime_float = 0.0;
var a: f32 = 4;
aa = a;
}
pub export fn entry4() void{
comptime var aa: comptime_int = 0.0;
var a: f32 = 4;
aa = a;
}
// error
// backend=stage2
// target=native
//
// :3:27: error: unable to resolve comptime value
// :3:27: note: value being casted to 'comptime_int' must be comptime known
// :7:29: error: unable to resolve comptime value
// :7:29: note: value being casted to 'comptime_float' must be comptime known
// :12:10: error: unable to resolve comptime value
// :12:10: note: value being casted to 'comptime_float' must be comptime known
// :17:10: error: unable to resolve comptime value
// :17:10: note: value being casted to 'comptime_int' must be comptime known

View File

@ -0,0 +1,23 @@
export fn entry(x: u8) u8 {
return 0x11 << x;
}
export fn entry1(x: u8) u8 {
return 0x11 >> x;
}
export fn entry2() void {
var x: u5 = 1;
_ = @shlExact(12345, x);
}
export fn entry3() void {
var x: u5 = 1;
_ = @shrExact(12345, x);
}
// error
// backend=stage2
// target=native
//
// :2:17: error: LHS of shift must be a fixed-width integer type, or RHS must be a comptime known
// :5:17: error: LHS of shift must be a fixed-width integer type, or RHS must be a comptime known
// :9:9: error: LHS of shift must be a fixed-width integer type, or RHS must be a comptime known
// :13:9: error: LHS of shift must be a fixed-width integer type, or RHS must be a comptime known

View File

@ -1,9 +0,0 @@
export fn entry(x: u8) u8 {
return 0x11 << x;
}
// error
// backend=stage1
// target=native
//
// tmp.zig:2:17: error: LHS of shift must be a fixed-width integer type, or RHS must be compile-time known

View File

@ -0,0 +1,18 @@
const std = @import("std");
const builtin = std.builtin;
pub fn foo(message: []const u8, stack_trace: ?*builtin.StackTrace) noreturn {
@call(.{ .modifier = .always_tail }, bar, .{ message, stack_trace });
}
pub fn bar(message: []const u8, stack_trace: ?*builtin.StackTrace) noreturn {
_ = message;
_ = stack_trace;
std.process.exit(0);
}
pub fn main() void {
foo("foo", null);
}
// run
// backend=llvm
// target=x86_64-linux,x86_64-macos,aarch64-linux,aarch64-macos