Index: lib/Transforms/IPO/IPConstantPropagation.cpp =================================================================== --- lib/Transforms/IPO/IPConstantPropagation.cpp +++ lib/Transforms/IPO/IPConstantPropagation.cpp @@ -66,6 +66,13 @@ if (!ACS) return false; + // Mismatched argument count is undefined behavior. Simply bail out to avoid + // handling of such situations below (avoiding asserts/crashes). + unsigned NumActualArgs = ACS.getNumArgOperands(); + if (F.isVarArg() ? ArgumentConstants.size() > NumActualArgs + : ArgumentConstants.size() != NumActualArgs) + return false; + // Check out all of the potentially constant arguments. Note that we don't // inspect varargs here. Function::arg_iterator Arg = F.arg_begin(); @@ -78,6 +85,11 @@ Value *V = ACS.getCallArgOperand(i); Constant *C = dyn_cast_or_null(V); + // Mismatched argument type is undefined behavior. Simply bail out to avoid + // handling of such situations below (avoiding asserts/crashes). + if (Arg->getType() != V->getType()) + return false; + // We can only propagate thread independent values through callbacks. // This is different to direct/indirect call sites because for them we // know the thread executing the caller and callee is the same. For Index: test/Transforms/IPConstantProp/arg-count-mismatch.ll =================================================================== --- /dev/null +++ test/Transforms/IPConstantProp/arg-count-mismatch.ll @@ -0,0 +1,49 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -ipconstprop -S -o - | FileCheck %s + +; The original C source looked like this: +; +; long long a101, b101, e101; +; volatile long c101; +; int d101; +; +; static inline int bar(p1, p2) +; { +; return 0; +; } +; +; void foo(unsigned p1) +; { +; long long *f = &b101, *g = &e101; +; c101 = 0; +; (void)((*f |= a101) - (*g = bar(d101))); +; c101 = (*f |= a101 &= p1) == d101; +; } +; +; When compiled with Clang it gives a warning +; warning: too few arguments in call to 'bar' +; +; This ll reproducer has been reduced to only include tha call. +; +; Note that -lint will report this as UB, but it passes -verify. + +; This test is just to verify that we do not crash/assert due to mismatch in +; argument count between the caller and callee. + +define dso_local void @foo(i16 %a) { +; CHECK-LABEL: @foo( +; CHECK-NEXT: [[CALL:%.*]] = call i16 bitcast (i16 (i16, i16)* @bar to i16 (i16)*)(i16 [[A:%.*]]) +; CHECK-NEXT: ret void +; + %call = call i16 bitcast (i16 (i16, i16) * @bar to i16 (i16) *)(i16 %a) + ret void +} + +define internal i16 @bar(i16 %p1, i16 %p2) { +; CHECK-LABEL: @bar( +; CHECK-NEXT: ret i16 0 +; + ret i16 0 +} + + Index: test/Transforms/IPConstantProp/arg-type-mismatch.ll =================================================================== --- /dev/null +++ test/Transforms/IPConstantProp/arg-type-mismatch.ll @@ -0,0 +1,23 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -ipconstprop -S -o - | FileCheck %s + +; This test is just to verify that we do not crash/assert due to mismatch in +; argument type between the caller and callee. + +define dso_local void @foo(i16 %a) { +; CHECK-LABEL: @foo( +; CHECK-NEXT: [[CALL:%.*]] = call i16 bitcast (i16 (i16, i16)* @bar to i16 (i16, i32)*)(i16 [[A:%.*]], i32 7) +; CHECK-NEXT: ret void +; + %call = call i16 bitcast (i16 (i16, i16) * @bar to i16 (i16, i32) *)(i16 %a, i32 7) + ret void +} + +define internal i16 @bar(i16 %p1, i16 %p2) { +; CHECK-LABEL: @bar( +; CHECK-NEXT: ret i16 [[P2:%.*]] +; + ret i16 %p2 +} + +