Commit 9ed599b4e3

Andrew Kelley <andrew@ziglang.org>
2021-10-06 05:28:29
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.
1 parent 01ad6c0
Changed files (3)
lib
std
special
compiler_rt
src
lib/std/special/compiler_rt/compareXf2.zig
@@ -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);
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;
 
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, &params, params.len, "");
+        const wrong_size_result = self.builder.buildCall(fn_val, &params, 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;