Skip to content

Commit 17ae776

Browse files
[MLIR][LLVMIR] Import calls with mismatching signature as indirect call (llvm#135895) (#40)
LLVM IR currently [accepts](https://godbolt.org/z/nqnEsW1ja): ``` define void @incompatible_call_and_callee_types() { call void @callee(i64 0) ret void } define void @callee({ptr, i64}, i32) { ret void } ``` This currently fails to import. Even though these constructs are dangerous and probably indicate some ODR violation (or optimization bug), they are "valid" and should be imported into LLVM IR dialect. This PR implements that by using an indirect call to represent it. Translation already works nicely and outputs the same source llvm IR file. The error is now a warning, the tests in `mlir/test/Target/LLVMIR/Import/import-failure.ll` already use `CHECK` lines, so no need to add extra diagnostic tests. Co-authored-by: Bruno Cardoso Lopes <[email protected]>
1 parent 48f3f84 commit 17ae776

File tree

4 files changed

+78
-22
lines changed

4 files changed

+78
-22
lines changed

mlir/include/mlir/Target/LLVMIR/ModuleImport.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -358,9 +358,11 @@ class ModuleImport {
358358
/// Converts the callee's function type. For direct calls, it converts the
359359
/// actual function type, which may differ from the called operand type in
360360
/// variadic functions. For indirect calls, it converts the function type
361-
/// associated with the call instruction. Returns failure when the call and
362-
/// the callee are not compatible or when nested type conversions failed.
363-
FailureOr<LLVMFunctionType> convertFunctionType(llvm::CallBase *callInst);
361+
/// associated with the call instruction. When the call and the callee are not
362+
/// compatible (or when nested type conversions failed), emit a warning and
363+
/// update `isIncompatibleCall` to indicate it.
364+
FailureOr<LLVMFunctionType> convertFunctionType(llvm::CallBase *callInst,
365+
bool &isIncompatibleCall);
364366
/// Returns the callee name, or an empty symbol if the call is not direct.
365367
FlatSymbolRefAttr convertCalleeName(llvm::CallBase *callInst);
366368
/// Converts the parameter and result attributes attached to `func` and adds

mlir/lib/Target/LLVMIR/ModuleImport.cpp

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1612,8 +1612,8 @@ ModuleImport::convertCallOperands(llvm::CallBase *callInst,
16121612
/// Checks if `callType` and `calleeType` are compatible and can be represented
16131613
/// in MLIR.
16141614
static LogicalResult
1615-
verifyFunctionTypeCompatibility(LLVMFunctionType callType,
1616-
LLVMFunctionType calleeType) {
1615+
checkFunctionTypeCompatibility(LLVMFunctionType callType,
1616+
LLVMFunctionType calleeType) {
16171617
if (callType.getReturnType() != calleeType.getReturnType())
16181618
return failure();
16191619

@@ -1639,7 +1639,9 @@ verifyFunctionTypeCompatibility(LLVMFunctionType callType,
16391639
}
16401640

16411641
FailureOr<LLVMFunctionType>
1642-
ModuleImport::convertFunctionType(llvm::CallBase *callInst) {
1642+
ModuleImport::convertFunctionType(llvm::CallBase *callInst,
1643+
bool &isIncompatibleCall) {
1644+
isIncompatibleCall = false;
16431645
auto castOrFailure = [](Type convertedType) -> FailureOr<LLVMFunctionType> {
16441646
auto funcTy = dyn_cast_or_null<LLVMFunctionType>(convertedType);
16451647
if (!funcTy)
@@ -1662,11 +1664,14 @@ ModuleImport::convertFunctionType(llvm::CallBase *callInst) {
16621664
if (failed(calleeType))
16631665
return failure();
16641666

1665-
// Compare the types to avoid constructing illegal call/invoke operations.
1666-
if (failed(verifyFunctionTypeCompatibility(*callType, *calleeType))) {
1667+
// Compare the types and notify users via `isIncompatibleCall` if they are not
1668+
// compatible.
1669+
if (failed(checkFunctionTypeCompatibility(*callType, *calleeType))) {
1670+
isIncompatibleCall = true;
16671671
Location loc = translateLoc(callInst->getDebugLoc());
1668-
return emitError(loc) << "incompatible call and callee types: " << *callType
1669-
<< " and " << *calleeType;
1672+
emitWarning(loc) << "incompatible call and callee types: " << *callType
1673+
<< " and " << *calleeType;
1674+
return callType;
16701675
}
16711676

16721677
return calleeType;
@@ -1783,16 +1788,34 @@ LogicalResult ModuleImport::convertInstruction(llvm::Instruction *inst) {
17831788
/*operand_attrs=*/nullptr)
17841789
.getOperation();
17851790
}
1786-
FailureOr<LLVMFunctionType> funcTy = convertFunctionType(callInst);
1791+
bool isIncompatibleCall;
1792+
FailureOr<LLVMFunctionType> funcTy =
1793+
convertFunctionType(callInst, isIncompatibleCall);
17871794
if (failed(funcTy))
17881795
return failure();
17891796

1790-
FlatSymbolRefAttr callee = convertCalleeName(callInst);
1791-
auto callOp = builder.create<CallOp>(loc, *funcTy, callee, *operands);
1797+
FlatSymbolRefAttr callee = nullptr;
1798+
if (isIncompatibleCall) {
1799+
// Use an indirect call (in order to represent valid and verifiable LLVM
1800+
// IR). Build the indirect call by passing an empty `callee` operand and
1801+
// insert into `operands` to include the indirect call target.
1802+
FlatSymbolRefAttr calleeSym = convertCalleeName(callInst);
1803+
Value indirectCallVal = builder.create<LLVM::AddressOfOp>(
1804+
loc, LLVM::LLVMPointerType::get(context), calleeSym);
1805+
operands->insert(operands->begin(), indirectCallVal);
1806+
} else {
1807+
// Regular direct call using callee name.
1808+
callee = convertCalleeName(callInst);
1809+
}
1810+
CallOp callOp = builder.create<CallOp>(loc, *funcTy, callee, *operands);
1811+
17921812
if (failed(convertCallAttributes(callInst, callOp)))
17931813
return failure();
1794-
// Handle parameter and result attributes.
1795-
convertParameterAttributes(callInst, callOp, builder);
1814+
1815+
// Handle parameter and result attributes unless it's an incompatible
1816+
// call.
1817+
if (!isIncompatibleCall)
1818+
convertParameterAttributes(callInst, callOp, builder);
17961819
return callOp.getOperation();
17971820
}();
17981821

@@ -1857,12 +1880,25 @@ LogicalResult ModuleImport::convertInstruction(llvm::Instruction *inst) {
18571880
unwindArgs)))
18581881
return failure();
18591882

1860-
FailureOr<LLVMFunctionType> funcTy = convertFunctionType(invokeInst);
1883+
bool isIncompatibleInvoke;
1884+
FailureOr<LLVMFunctionType> funcTy =
1885+
convertFunctionType(invokeInst, isIncompatibleInvoke);
18611886
if (failed(funcTy))
18621887
return failure();
18631888

1864-
FlatSymbolRefAttr calleeName = convertCalleeName(invokeInst);
1865-
1889+
FlatSymbolRefAttr calleeName = nullptr;
1890+
if (isIncompatibleInvoke) {
1891+
// Use an indirect invoke (in order to represent valid and verifiable LLVM
1892+
// IR). Build the indirect invoke by passing an empty `callee` operand and
1893+
// insert into `operands` to include the indirect invoke target.
1894+
FlatSymbolRefAttr calleeSym = convertCalleeName(invokeInst);
1895+
Value indirectInvokeVal = builder.create<LLVM::AddressOfOp>(
1896+
loc, LLVM::LLVMPointerType::get(context), calleeSym);
1897+
operands->insert(operands->begin(), indirectInvokeVal);
1898+
} else {
1899+
// Regular direct invoke using callee name.
1900+
calleeName = convertCalleeName(invokeInst);
1901+
}
18661902
// Create the invoke operation. Normal destination block arguments will be
18671903
// added later on to handle the case in which the operation result is
18681904
// included in this list.
@@ -1873,8 +1909,10 @@ LogicalResult ModuleImport::convertInstruction(llvm::Instruction *inst) {
18731909
if (failed(convertInvokeAttributes(invokeInst, invokeOp)))
18741910
return failure();
18751911

1876-
// Handle parameter and result attributes.
1877-
convertParameterAttributes(invokeInst, invokeOp, builder);
1912+
// Handle parameter and result attributes unless it's an incompatible
1913+
// invoke.
1914+
if (!isIncompatibleInvoke)
1915+
convertParameterAttributes(invokeInst, invokeOp, builder);
18781916

18791917
if (!invokeInst->getType()->isVoidTy())
18801918
mapValue(inst, invokeOp.getResults().front());

mlir/test/Target/LLVMIR/Import/import-failure.ll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ target datalayout = "e-m-i64:64"
362362
; // -----
363363

364364
; CHECK: <unknown>
365-
; CHECK-SAME: incompatible call and callee types: '!llvm.func<void (i64)>' and '!llvm.func<void (ptr)>'
365+
; CHECK-SAME: warning: incompatible call and callee types: '!llvm.func<void (i64)>' and '!llvm.func<void (ptr)>'
366366
define void @incompatible_call_and_callee_types() {
367367
call void @callee(i64 0)
368368
ret void
@@ -373,7 +373,7 @@ declare void @callee(ptr)
373373
; // -----
374374

375375
; CHECK: <unknown>
376-
; CHECK-SAME: incompatible call and callee types: '!llvm.func<void ()>' and '!llvm.func<i32 ()>'
376+
; CHECK-SAME: warning: incompatible call and callee types: '!llvm.func<void ()>' and '!llvm.func<i32 ()>'
377377
define void @f() personality ptr @__gxx_personality_v0 {
378378
entry:
379379
invoke void @g() to label %bb1 unwind label %bb2

mlir/test/Target/LLVMIR/Import/instructions.ll

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,3 +720,19 @@ bb2:
720720
declare void @g(...)
721721

722722
declare i32 @__gxx_personality_v0(...)
723+
724+
; // -----
725+
726+
; CHECK-LABEL: llvm.func @incompatible_call_and_callee_types
727+
define void @incompatible_call_and_callee_types() {
728+
; CHECK: %[[CST:.*]] = llvm.mlir.constant(0 : i64) : i64
729+
; CHECK: %[[TARGET:.*]] = llvm.mlir.addressof @callee : !llvm.ptr
730+
; CHECK: llvm.call %[[TARGET]](%[[CST]]) : !llvm.ptr, (i64) -> ()
731+
call void @callee(i64 0)
732+
; CHECK: llvm.return
733+
ret void
734+
}
735+
736+
define void @callee({ptr, i64}, i32) {
737+
ret void
738+
}

0 commit comments

Comments
 (0)