mirror of
https://github.com/ziglang/zig.git
synced 2026-01-10 01:15:14 +00:00
stage2: LLVM backend: miscompilation fixes
* work around a stage1 miscompilation leading to the wrong integer comparison predicate being emitted. * fix the bug of not annotating callsites with the calling convention of the callee, leading to undefined behavior. * add the `nobuiltin` attribute when building freestanding libc or compiler_rt libraries to prevent e.g. memcpy from being "optimized" into a call to itself. * compiler-rt: change a call to be comptime to make the generated LLVM IR simpler and easier to study. I still can't enable the widening tests due to the compiler-rt compare function being miscompiled in some not-yet-diagnosed way.
This commit is contained in:
parent
01ad6c0b02
commit
9ed599b4e3
@ -32,7 +32,7 @@ pub inline fn cmp(comptime T: type, comptime RT: type, a: T, b: T) RT {
|
||||
const exponentBits = std.math.floatExponentBits(T);
|
||||
const signBit = (@as(rep_t, 1) << (significandBits + exponentBits));
|
||||
const absMask = signBit - 1;
|
||||
const infT = std.math.inf(T);
|
||||
const infT = comptime std.math.inf(T);
|
||||
const infRep = @bitCast(rep_t, infT);
|
||||
|
||||
const aInt = @bitCast(srep_t, a);
|
||||
|
||||
@ -643,67 +643,21 @@ pub const DeclGen = struct {
|
||||
llvm_fn.setUnnamedAddr(.True);
|
||||
}
|
||||
|
||||
if (self.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.
|
||||
self.addFnAttr(llvm_fn, "nobuiltin");
|
||||
}
|
||||
|
||||
// TODO: more attributes. see codegen.cpp `make_fn_llvm_value`.
|
||||
const target = self.module.getTarget();
|
||||
switch (fn_info.cc) {
|
||||
.Unspecified, .Inline, .Async => {
|
||||
llvm_fn.setFunctionCallConv(.Fast);
|
||||
},
|
||||
.C => {
|
||||
llvm_fn.setFunctionCallConv(.C);
|
||||
},
|
||||
.Naked => {
|
||||
self.addFnAttr(llvm_fn, "naked");
|
||||
},
|
||||
.Stdcall => {
|
||||
llvm_fn.setFunctionCallConv(.X86_StdCall);
|
||||
},
|
||||
.Fastcall => {
|
||||
llvm_fn.setFunctionCallConv(.X86_FastCall);
|
||||
},
|
||||
.Vectorcall => {
|
||||
switch (target.cpu.arch) {
|
||||
.i386, .x86_64 => {
|
||||
llvm_fn.setFunctionCallConv(.X86_VectorCall);
|
||||
},
|
||||
.aarch64, .aarch64_be, .aarch64_32 => {
|
||||
llvm_fn.setFunctionCallConv(.AArch64_VectorCall);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
.Thiscall => {
|
||||
llvm_fn.setFunctionCallConv(.X86_ThisCall);
|
||||
},
|
||||
.APCS => {
|
||||
llvm_fn.setFunctionCallConv(.ARM_APCS);
|
||||
},
|
||||
.AAPCS => {
|
||||
llvm_fn.setFunctionCallConv(.ARM_AAPCS);
|
||||
},
|
||||
.AAPCSVFP => {
|
||||
llvm_fn.setFunctionCallConv(.ARM_AAPCS_VFP);
|
||||
},
|
||||
.Interrupt => {
|
||||
switch (target.cpu.arch) {
|
||||
.i386, .x86_64 => {
|
||||
llvm_fn.setFunctionCallConv(.X86_INTR);
|
||||
},
|
||||
.avr => {
|
||||
llvm_fn.setFunctionCallConv(.AVR_INTR);
|
||||
},
|
||||
.msp430 => {
|
||||
llvm_fn.setFunctionCallConv(.MSP430_INTR);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
.Signal => {
|
||||
llvm_fn.setFunctionCallConv(.AVR_SIGNAL);
|
||||
},
|
||||
.SysV => {
|
||||
llvm_fn.setFunctionCallConv(.X86_64_SysV);
|
||||
},
|
||||
if (fn_info.cc == .Naked) {
|
||||
self.addFnAttr(llvm_fn, "naked");
|
||||
} else {
|
||||
llvm_fn.setFunctionCallConv(toLlvmCallConv(fn_info.cc, target));
|
||||
}
|
||||
|
||||
// Function attributes that are independent of analysis results of the function body.
|
||||
@ -1445,6 +1399,7 @@ pub const FuncGen = struct {
|
||||
const zig_fn_type = self.air.typeOf(pl_op.operand);
|
||||
const return_type = zig_fn_type.fnReturnType();
|
||||
const llvm_fn = try self.resolveInst(pl_op.operand);
|
||||
const target = self.dg.module.getTarget();
|
||||
|
||||
const llvm_param_vals = try self.gpa.alloc(*const llvm.Value, args.len);
|
||||
defer self.gpa.free(llvm_param_vals);
|
||||
@ -1457,6 +1412,8 @@ pub const FuncGen = struct {
|
||||
llvm_fn,
|
||||
llvm_param_vals.ptr,
|
||||
@intCast(c_uint, args.len),
|
||||
toLlvmCallConv(zig_fn_type.fnCallingConvention(), target),
|
||||
.Auto,
|
||||
"",
|
||||
);
|
||||
|
||||
@ -1489,13 +1446,10 @@ pub const FuncGen = struct {
|
||||
const lhs = try self.resolveInst(bin_op.lhs);
|
||||
const rhs = try self.resolveInst(bin_op.rhs);
|
||||
const operand_ty = self.air.typeOf(bin_op.lhs);
|
||||
var buffer: Type.Payload.Bits = undefined;
|
||||
|
||||
const int_ty = switch (operand_ty.zigTypeTag()) {
|
||||
.Enum => blk: {
|
||||
var buffer: Type.Payload.Bits = undefined;
|
||||
const int_ty = operand_ty.intTagType(&buffer);
|
||||
break :blk int_ty;
|
||||
},
|
||||
.Enum => operand_ty.intTagType(&buffer),
|
||||
.Int, .Bool, .Pointer, .ErrorSet => operand_ty,
|
||||
.Float => {
|
||||
const operation: llvm.RealPredicate = switch (op) {
|
||||
@ -1511,13 +1465,13 @@ pub const FuncGen = struct {
|
||||
else => unreachable,
|
||||
};
|
||||
const is_signed = int_ty.isSignedInt();
|
||||
const operation = switch (op) {
|
||||
const operation: llvm.IntPredicate = switch (op) {
|
||||
.eq => .EQ,
|
||||
.neq => .NE,
|
||||
.lt => @as(llvm.IntPredicate, if (is_signed) .SLT else .ULT),
|
||||
.lte => @as(llvm.IntPredicate, if (is_signed) .SLE else .ULE),
|
||||
.gt => @as(llvm.IntPredicate, if (is_signed) .SGT else .UGT),
|
||||
.gte => @as(llvm.IntPredicate, if (is_signed) .SGE else .UGE),
|
||||
.lt => if (is_signed) llvm.IntPredicate.SLT else .ULT,
|
||||
.lte => if (is_signed) llvm.IntPredicate.SLE else .ULE,
|
||||
.gt => if (is_signed) llvm.IntPredicate.SGT else .UGT,
|
||||
.gte => if (is_signed) llvm.IntPredicate.SGE else .UGE,
|
||||
};
|
||||
return self.builder.buildICmp(operation, lhs, rhs, "");
|
||||
}
|
||||
@ -1947,6 +1901,8 @@ pub const FuncGen = struct {
|
||||
asm_fn,
|
||||
llvm_param_values.ptr,
|
||||
@intCast(c_uint, llvm_param_values.len),
|
||||
.C,
|
||||
.Auto,
|
||||
"",
|
||||
);
|
||||
}
|
||||
@ -2561,7 +2517,7 @@ pub const FuncGen = struct {
|
||||
fn airBreakpoint(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
_ = inst;
|
||||
const llvm_fn = self.getIntrinsic("llvm.debugtrap");
|
||||
_ = self.builder.buildCall(llvm_fn, undefined, 0, "");
|
||||
_ = self.builder.buildCall(llvm_fn, undefined, 0, .C, .Auto, "");
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -2818,7 +2774,7 @@ pub const FuncGen = struct {
|
||||
};
|
||||
|
||||
const params = [_]*const llvm.Value{ operand, llvm_i1.constNull() };
|
||||
const wrong_size_result = self.builder.buildCall(fn_val, ¶ms, params.len, "");
|
||||
const wrong_size_result = self.builder.buildCall(fn_val, ¶ms, params.len, .C, .Auto, "");
|
||||
const result_ty = self.air.typeOfIndex(inst);
|
||||
const result_llvm_ty = try self.dg.llvmType(result_ty);
|
||||
const result_bits = result_ty.intInfo(target).bits;
|
||||
@ -3108,6 +3064,32 @@ fn toLlvmAtomicRmwBinOp(
|
||||
};
|
||||
}
|
||||
|
||||
fn toLlvmCallConv(cc: std.builtin.CallingConvention, target: std.Target) llvm.CallConv {
|
||||
return switch (cc) {
|
||||
.Unspecified, .Inline, .Async => .Fast,
|
||||
.C, .Naked => .C,
|
||||
.Stdcall => .X86_StdCall,
|
||||
.Fastcall => .X86_FastCall,
|
||||
.Vectorcall => return switch (target.cpu.arch) {
|
||||
.i386, .x86_64 => .X86_VectorCall,
|
||||
.aarch64, .aarch64_be, .aarch64_32 => .AArch64_VectorCall,
|
||||
else => unreachable,
|
||||
},
|
||||
.Thiscall => .X86_ThisCall,
|
||||
.APCS => .ARM_APCS,
|
||||
.AAPCS => .ARM_AAPCS,
|
||||
.AAPCSVFP => .ARM_AAPCS_VFP,
|
||||
.Interrupt => return switch (target.cpu.arch) {
|
||||
.i386, .x86_64 => .X86_INTR,
|
||||
.avr => .AVR_INTR,
|
||||
.msp430 => .MSP430_INTR,
|
||||
else => unreachable,
|
||||
},
|
||||
.Signal => .AVR_SIGNAL,
|
||||
.SysV => .X86_64_SysV,
|
||||
};
|
||||
}
|
||||
|
||||
/// Take into account 0 bit fields.
|
||||
fn llvmFieldIndex(ty: Type, index: u32) c_uint {
|
||||
const struct_obj = ty.castTag(.@"struct").?.data;
|
||||
|
||||
@ -359,22 +359,14 @@ pub const Builder = opaque {
|
||||
Name: [*:0]const u8,
|
||||
) *const Value;
|
||||
|
||||
pub const buildCall = LLVMBuildCall;
|
||||
extern fn LLVMBuildCall(
|
||||
pub const buildCall = ZigLLVMBuildCall;
|
||||
extern fn ZigLLVMBuildCall(
|
||||
*const Builder,
|
||||
Fn: *const Value,
|
||||
Args: [*]const *const Value,
|
||||
NumArgs: c_uint,
|
||||
Name: [*:0]const u8,
|
||||
) *const Value;
|
||||
|
||||
pub const buildCall2 = LLVMBuildCall2;
|
||||
extern fn LLVMBuildCall2(
|
||||
*const Builder,
|
||||
*const Type,
|
||||
Fn: *const Value,
|
||||
Args: [*]*const Value,
|
||||
NumArgs: c_uint,
|
||||
CC: CallConv,
|
||||
attr: CallAttr,
|
||||
Name: [*:0]const u8,
|
||||
) *const Value;
|
||||
|
||||
@ -1184,6 +1176,14 @@ pub const CallConv = enum(c_uint) {
|
||||
AArch64_VectorCall = 97,
|
||||
};
|
||||
|
||||
pub const CallAttr = enum(c_int) {
|
||||
Auto,
|
||||
NeverTail,
|
||||
NeverInline,
|
||||
AlwaysTail,
|
||||
AlwaysInline,
|
||||
};
|
||||
|
||||
pub const address_space = struct {
|
||||
pub const default: c_uint = 0;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user