diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-10-05 20:28:29 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2021-10-05 20:36:04 -0700 |
| commit | 9ed599b4e3f5c9f48089a3acd61d0338e27e88f6 (patch) | |
| tree | 97f51ac789f90050f659b3faceb4a665aa304868 /src | |
| parent | 01ad6c0b020193a6c5c82e13296a12e847f78d05 (diff) | |
| download | zig-9ed599b4e3f5c9f48089a3acd61d0338e27e88f6.tar.gz zig-9ed599b4e3f5c9f48089a3acd61d0338e27e88f6.zip | |
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.
Diffstat (limited to 'src')
| -rw-r--r-- | src/codegen/llvm.zig | 124 | ||||
| -rw-r--r-- | src/codegen/llvm/bindings.zig | 24 |
2 files changed, 65 insertions, 83 deletions
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index ec3c955c7b..c9e9b240bd 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -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; diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index 496579008e..a3ccea0951 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -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; |
