Skip to content

Commit 7682f66

Browse files
[MLIR][LLVMIR] Import calls with mismatching signature as indirect call (#135895)
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.
1 parent 37da5a1 commit 7682f66

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
@@ -362,9 +362,11 @@ class ModuleImport {
362362
/// Converts the callee's function type. For direct calls, it converts the
363363
/// actual function type, which may differ from the called operand type in
364364
/// variadic functions. For indirect calls, it converts the function type
365-
/// associated with the call instruction. Returns failure when the call and
366-
/// the callee are not compatible or when nested type conversions failed.
367-
FailureOr<LLVMFunctionType> convertFunctionType(llvm::CallBase *callInst);
365+
/// associated with the call instruction. When the call and the callee are not
366+
/// compatible (or when nested type conversions failed), emit a warning and
367+
/// update `isIncompatibleCall` to indicate it.
368+
FailureOr<LLVMFunctionType> convertFunctionType(llvm::CallBase *callInst,
369+
bool &isIncompatibleCall);
368370
/// Returns the callee name, or an empty symbol if the call is not direct.
369371
FlatSymbolRefAttr convertCalleeName(llvm::CallBase *callInst);
370372
/// 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
@@ -1993,8 +1993,8 @@ ModuleImport::convertCallOperands(llvm::CallBase *callInst,
19931993
/// Checks if `callType` and `calleeType` are compatible and can be represented
19941994
/// in MLIR.
19951995
static LogicalResult
1996-
verifyFunctionTypeCompatibility(LLVMFunctionType callType,
1997-
LLVMFunctionType calleeType) {
1996+
checkFunctionTypeCompatibility(LLVMFunctionType callType,
1997+
LLVMFunctionType calleeType) {
19981998
if (callType.getReturnType() != calleeType.getReturnType())
19991999
return failure();
20002000

@@ -2020,7 +2020,9 @@ verifyFunctionTypeCompatibility(LLVMFunctionType callType,
20202020
}
20212021

20222022
FailureOr<LLVMFunctionType>
2023-
ModuleImport::convertFunctionType(llvm::CallBase *callInst) {
2023+
ModuleImport::convertFunctionType(llvm::CallBase *callInst,
2024+
bool &isIncompatibleCall) {
2025+
isIncompatibleCall = false;
20242026
auto castOrFailure = [](Type convertedType) -> FailureOr<LLVMFunctionType> {
20252027
auto funcTy = dyn_cast_or_null<LLVMFunctionType>(convertedType);
20262028
if (!funcTy)
@@ -2043,11 +2045,14 @@ ModuleImport::convertFunctionType(llvm::CallBase *callInst) {
20432045
if (failed(calleeType))
20442046
return failure();
20452047

2046-
// Compare the types to avoid constructing illegal call/invoke operations.
2047-
if (failed(verifyFunctionTypeCompatibility(*callType, *calleeType))) {
2048+
// Compare the types and notify users via `isIncompatibleCall` if they are not
2049+
// compatible.
2050+
if (failed(checkFunctionTypeCompatibility(*callType, *calleeType))) {
2051+
isIncompatibleCall = true;
20482052
Location loc = translateLoc(callInst->getDebugLoc());
2049-
return emitError(loc) << "incompatible call and callee types: " << *callType
2050-
<< " and " << *calleeType;
2053+
emitWarning(loc) << "incompatible call and callee types: " << *callType
2054+
<< " and " << *calleeType;
2055+
return callType;
20512056
}
20522057

20532058
return calleeType;
@@ -2164,16 +2169,34 @@ LogicalResult ModuleImport::convertInstruction(llvm::Instruction *inst) {
21642169
/*operand_attrs=*/nullptr)
21652170
.getOperation();
21662171
}
2167-
FailureOr<LLVMFunctionType> funcTy = convertFunctionType(callInst);
2172+
bool isIncompatibleCall;
2173+
FailureOr<LLVMFunctionType> funcTy =
2174+
convertFunctionType(callInst, isIncompatibleCall);
21682175
if (failed(funcTy))
21692176
return failure();
21702177

2171-
FlatSymbolRefAttr callee = convertCalleeName(callInst);
2172-
auto callOp = builder.create<CallOp>(loc, *funcTy, callee, *operands);
2178+
FlatSymbolRefAttr callee = nullptr;
2179+
if (isIncompatibleCall) {
2180+
// Use an indirect call (in order to represent valid and verifiable LLVM
2181+
// IR). Build the indirect call by passing an empty `callee` operand and
2182+
// insert into `operands` to include the indirect call target.
2183+
FlatSymbolRefAttr calleeSym = convertCalleeName(callInst);
2184+
Value indirectCallVal = builder.create<LLVM::AddressOfOp>(
2185+
loc, LLVM::LLVMPointerType::get(context), calleeSym);
2186+
operands->insert(operands->begin(), indirectCallVal);
2187+
} else {
2188+
// Regular direct call using callee name.
2189+
callee = convertCalleeName(callInst);
2190+
}
2191+
CallOp callOp = builder.create<CallOp>(loc, *funcTy, callee, *operands);
2192+
21732193
if (failed(convertCallAttributes(callInst, callOp)))
21742194
return failure();
2175-
// Handle parameter and result attributes.
2176-
convertParameterAttributes(callInst, callOp, builder);
2195+
2196+
// Handle parameter and result attributes unless it's an incompatible
2197+
// call.
2198+
if (!isIncompatibleCall)
2199+
convertParameterAttributes(callInst, callOp, builder);
21772200
return callOp.getOperation();
21782201
}();
21792202

@@ -2238,12 +2261,25 @@ LogicalResult ModuleImport::convertInstruction(llvm::Instruction *inst) {
22382261
unwindArgs)))
22392262
return failure();
22402263

2241-
FailureOr<LLVMFunctionType> funcTy = convertFunctionType(invokeInst);
2264+
bool isIncompatibleInvoke;
2265+
FailureOr<LLVMFunctionType> funcTy =
2266+
convertFunctionType(invokeInst, isIncompatibleInvoke);
22422267
if (failed(funcTy))
22432268
return failure();
22442269

2245-
FlatSymbolRefAttr calleeName = convertCalleeName(invokeInst);
2246-
2270+
FlatSymbolRefAttr calleeName = nullptr;
2271+
if (isIncompatibleInvoke) {
2272+
// Use an indirect invoke (in order to represent valid and verifiable LLVM
2273+
// IR). Build the indirect invoke by passing an empty `callee` operand and
2274+
// insert into `operands` to include the indirect invoke target.
2275+
FlatSymbolRefAttr calleeSym = convertCalleeName(invokeInst);
2276+
Value indirectInvokeVal = builder.create<LLVM::AddressOfOp>(
2277+
loc, LLVM::LLVMPointerType::get(context), calleeSym);
2278+
operands->insert(operands->begin(), indirectInvokeVal);
2279+
} else {
2280+
// Regular direct invoke using callee name.
2281+
calleeName = convertCalleeName(invokeInst);
2282+
}
22472283
// Create the invoke operation. Normal destination block arguments will be
22482284
// added later on to handle the case in which the operation result is
22492285
// included in this list.
@@ -2254,8 +2290,10 @@ LogicalResult ModuleImport::convertInstruction(llvm::Instruction *inst) {
22542290
if (failed(convertInvokeAttributes(invokeInst, invokeOp)))
22552291
return failure();
22562292

2257-
// Handle parameter and result attributes.
2258-
convertParameterAttributes(invokeInst, invokeOp, builder);
2293+
// Handle parameter and result attributes unless it's an incompatible
2294+
// invoke.
2295+
if (!isIncompatibleInvoke)
2296+
convertParameterAttributes(invokeInst, invokeOp, builder);
22592297

22602298
if (!invokeInst->getType()->isVoidTy())
22612299
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
@@ -313,7 +313,7 @@ target datalayout = "e-m-i64:64"
313313
; // -----
314314

315315
; CHECK: <unknown>
316-
; CHECK-SAME: incompatible call and callee types: '!llvm.func<void (i64)>' and '!llvm.func<void (ptr)>'
316+
; CHECK-SAME: warning: incompatible call and callee types: '!llvm.func<void (i64)>' and '!llvm.func<void (ptr)>'
317317
define void @incompatible_call_and_callee_types() {
318318
call void @callee(i64 0)
319319
ret void
@@ -324,7 +324,7 @@ declare void @callee(ptr)
324324
; // -----
325325

326326
; CHECK: <unknown>
327-
; CHECK-SAME: incompatible call and callee types: '!llvm.func<void ()>' and '!llvm.func<i32 ()>'
327+
; CHECK-SAME: warning: incompatible call and callee types: '!llvm.func<void ()>' and '!llvm.func<i32 ()>'
328328
define void @f() personality ptr @__gxx_personality_v0 {
329329
entry:
330330
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
@@ -743,3 +743,19 @@ bb2:
743743
declare void @g(...)
744744

745745
declare i32 @__gxx_personality_v0(...)
746+
747+
; // -----
748+
749+
; CHECK-LABEL: llvm.func @incompatible_call_and_callee_types
750+
define void @incompatible_call_and_callee_types() {
751+
; CHECK: %[[CST:.*]] = llvm.mlir.constant(0 : i64) : i64
752+
; CHECK: %[[TARGET:.*]] = llvm.mlir.addressof @callee : !llvm.ptr
753+
; CHECK: llvm.call %[[TARGET]](%[[CST]]) : !llvm.ptr, (i64) -> ()
754+
call void @callee(i64 0)
755+
; CHECK: llvm.return
756+
ret void
757+
}
758+
759+
define void @callee({ptr, i64}, i32) {
760+
ret void
761+
}

0 commit comments

Comments
 (0)