Skip to content

Conversation

@michele-scandale
Copy link
Contributor

When a function is declared with the asm(...) attribute to change the mangled name to reference a LLVM intrinsic, the AST->IR translation for the function declaration already skips any function/parameter attribute that is either deduced from the AST function declaration or implied by language options/target properties. Instead the attributes associated to the LLVM intrinsic function are being generated.

When emitting calls to such function declarations, however, call site attributes are emitted based on the AST function declaration or language options/target properties.
This can lead to undesired outcomes, e.g. the call site of a LLVM intrinsic marked convergent when the language options implies convergent by default.

This commit fixes the call lowering logic to ignore all attributes in the case the generate call instruction is an intrinsic call, such that the only attributes from the intrinsic declaration will be implicitly considered.

When a function is declared with the `asm(...)` attribute to change the
mangled name to reference a LLVM intrinsic, the AST->IR translation for
the function declaration already skips any function/parameter attribute
that is either deduced from the AST function declaration or implied by
language options/target properties. Instead the attributes associated
to the LLVM intrinsic function are being generated.

When emitting calls to such function declarations, however, call site
attributes are emitted based on the AST function declaration or language
options/target properties.
This can lead to undesired outcomes, e.g. the call site of a LLVM
intrinsic marked `convergent` when the language options implies
`convergent` by default.

This commit fixes the call lowering logic to ignore all attributes in
the case the generate call instruction is an intrinsic call, such that
the only attributes from the intrinsic declaration will be implicitly
considered.
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:codegen IR generation bugs: mangling, exceptions, etc. labels Nov 19, 2024
@llvmbot
Copy link
Member

llvmbot commented Nov 19, 2024

@llvm/pr-subscribers-clang

Author: Michele Scandale (michele-scandale)

Changes

When a function is declared with the asm(...) attribute to change the mangled name to reference a LLVM intrinsic, the AST->IR translation for the function declaration already skips any function/parameter attribute that is either deduced from the AST function declaration or implied by language options/target properties. Instead the attributes associated to the LLVM intrinsic function are being generated.

When emitting calls to such function declarations, however, call site attributes are emitted based on the AST function declaration or language options/target properties.
This can lead to undesired outcomes, e.g. the call site of a LLVM intrinsic marked convergent when the language options implies convergent by default.

This commit fixes the call lowering logic to ignore all attributes in the case the generate call instruction is an intrinsic call, such that the only attributes from the intrinsic declaration will be implicitly considered.


Full diff: https://github.com/llvm/llvm-project/pull/116881.diff

3 Files Affected:

  • (modified) clang/lib/CodeGen/CGCall.cpp (+8-2)
  • (added) clang/test/CodeGen/llvm-intrinsic-call.c (+13)
  • (modified) clang/test/CodeGenCUDA/surface.cu (+1-1)
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 35d495d4dfab82..7af3c676182e5f 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -5793,8 +5793,14 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
   }
 
   // Apply the attributes and calling convention.
-  CI->setAttributes(Attrs);
-  CI->setCallingConv(static_cast<llvm::CallingConv::ID>(CallingConv));
+
+  // If this is a call to an intrinsic, ignore the attributes that would
+  // normally be deduced from the AST function declaration + the default
+  // attributes imposed by the language and/or target.
+  if (!isa<llvm::IntrinsicInst>(CI)) {
+    CI->setAttributes(Attrs);
+    CI->setCallingConv(static_cast<llvm::CallingConv::ID>(CallingConv));
+  }
 
   // Apply various metadata.
 
diff --git a/clang/test/CodeGen/llvm-intrinsic-call.c b/clang/test/CodeGen/llvm-intrinsic-call.c
new file mode 100644
index 00000000000000..f71769d652765e
--- /dev/null
+++ b/clang/test/CodeGen/llvm-intrinsic-call.c
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 %s -emit-llvm -o - | FileCheck %s
+
+float llvm_sin_f32(float) asm("llvm.sin.f32");
+
+float test(float a) {
+  return llvm_sin_f32(a);
+}
+
+// CHECK: call float @llvm.sin.f32(float {{%.+}}){{$}}
+
+// CHECK: declare float @llvm.sin.f32(float) [[ATTRS:#[0-9]+]]
+
+// CHECK: attributes [[ATTRS]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
diff --git a/clang/test/CodeGenCUDA/surface.cu b/clang/test/CodeGenCUDA/surface.cu
index 4106673f3138af..c9e9f1543ec7df 100644
--- a/clang/test/CodeGenCUDA/surface.cu
+++ b/clang/test/CodeGenCUDA/surface.cu
@@ -29,7 +29,7 @@ __attribute__((device)) int suld_2d_zero(surface<void, 2>, int, int) asm("llvm.n
 
 // DEVICE-LABEL: i32 @_Z3fooii(i32 noundef %x, i32 noundef %y)
 // DEVICE: call i64 @llvm.nvvm.texsurf.handle.internal.p1(ptr addrspace(1) @surf)
-// DEVICE: call noundef i32 @llvm.nvvm.suld.2d.i32.zero(i64 %{{.*}}, i32 noundef %{{.*}}, i32 noundef %{{.*}})
+// DEVICE: call i32 @llvm.nvvm.suld.2d.i32.zero(i64 %{{.*}}, i32 %{{.*}}, i32 %{{.*}})
 __attribute__((device)) int foo(int x, int y) {
   return suld_2d_zero(surf, x, y);
 }

@llvmbot
Copy link
Member

llvmbot commented Nov 19, 2024

@llvm/pr-subscribers-clang-codegen

Author: Michele Scandale (michele-scandale)

Changes

When a function is declared with the asm(...) attribute to change the mangled name to reference a LLVM intrinsic, the AST->IR translation for the function declaration already skips any function/parameter attribute that is either deduced from the AST function declaration or implied by language options/target properties. Instead the attributes associated to the LLVM intrinsic function are being generated.

When emitting calls to such function declarations, however, call site attributes are emitted based on the AST function declaration or language options/target properties.
This can lead to undesired outcomes, e.g. the call site of a LLVM intrinsic marked convergent when the language options implies convergent by default.

This commit fixes the call lowering logic to ignore all attributes in the case the generate call instruction is an intrinsic call, such that the only attributes from the intrinsic declaration will be implicitly considered.


Full diff: https://github.com/llvm/llvm-project/pull/116881.diff

3 Files Affected:

  • (modified) clang/lib/CodeGen/CGCall.cpp (+8-2)
  • (added) clang/test/CodeGen/llvm-intrinsic-call.c (+13)
  • (modified) clang/test/CodeGenCUDA/surface.cu (+1-1)
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 35d495d4dfab82..7af3c676182e5f 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -5793,8 +5793,14 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
   }
 
   // Apply the attributes and calling convention.
-  CI->setAttributes(Attrs);
-  CI->setCallingConv(static_cast<llvm::CallingConv::ID>(CallingConv));
+
+  // If this is a call to an intrinsic, ignore the attributes that would
+  // normally be deduced from the AST function declaration + the default
+  // attributes imposed by the language and/or target.
+  if (!isa<llvm::IntrinsicInst>(CI)) {
+    CI->setAttributes(Attrs);
+    CI->setCallingConv(static_cast<llvm::CallingConv::ID>(CallingConv));
+  }
 
   // Apply various metadata.
 
diff --git a/clang/test/CodeGen/llvm-intrinsic-call.c b/clang/test/CodeGen/llvm-intrinsic-call.c
new file mode 100644
index 00000000000000..f71769d652765e
--- /dev/null
+++ b/clang/test/CodeGen/llvm-intrinsic-call.c
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 %s -emit-llvm -o - | FileCheck %s
+
+float llvm_sin_f32(float) asm("llvm.sin.f32");
+
+float test(float a) {
+  return llvm_sin_f32(a);
+}
+
+// CHECK: call float @llvm.sin.f32(float {{%.+}}){{$}}
+
+// CHECK: declare float @llvm.sin.f32(float) [[ATTRS:#[0-9]+]]
+
+// CHECK: attributes [[ATTRS]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
diff --git a/clang/test/CodeGenCUDA/surface.cu b/clang/test/CodeGenCUDA/surface.cu
index 4106673f3138af..c9e9f1543ec7df 100644
--- a/clang/test/CodeGenCUDA/surface.cu
+++ b/clang/test/CodeGenCUDA/surface.cu
@@ -29,7 +29,7 @@ __attribute__((device)) int suld_2d_zero(surface<void, 2>, int, int) asm("llvm.n
 
 // DEVICE-LABEL: i32 @_Z3fooii(i32 noundef %x, i32 noundef %y)
 // DEVICE: call i64 @llvm.nvvm.texsurf.handle.internal.p1(ptr addrspace(1) @surf)
-// DEVICE: call noundef i32 @llvm.nvvm.suld.2d.i32.zero(i64 %{{.*}}, i32 noundef %{{.*}}, i32 noundef %{{.*}})
+// DEVICE: call i32 @llvm.nvvm.suld.2d.i32.zero(i64 %{{.*}}, i32 %{{.*}}, i32 %{{.*}})
 __attribute__((device)) int foo(int x, int y) {
   return suld_2d_zero(surf, x, y);
 }

@efriedma-quic
Copy link
Collaborator

Accessing intrinsics using asm is not a documented feature; it's an unintended result of the name clash between functions and LLVM intrinsics. And I'm a little hesitant about making it a real feature; there are other calling convention issues, and IR changes can very easily break "working" code by accident.

@rjmccall
Copy link
Contributor

Yeah, that's my take as well. Anything we do to make this work better risks being interpreted as treating this as somehow supported, which it absolutely is not.

@michele-scandale
Copy link
Contributor Author

I understand this is not something recommended.

Still there is already code like the one in https://github.com/llvm/llvm-project/blob/main/clang/lib/CodeGen/CodeGenModule.cpp#L2887-L2892

void CodeGenModule::SetFunctionAttributes(GlobalDecl GD, llvm::Function *F,
                                          bool IsIncompleteFunction,
                                          bool IsThunk) {

  if (llvm::Intrinsic::ID IID = F->getIntrinsicID()) {
    // If this is an intrinsic function, set the function's attributes
    // to the intrinsic's attributes.
    F->setAttributes(llvm::Intrinsic::getAttributes(getLLVMContext(), IID));
    return;
  }

which a small trace of intention to do something special about this case.
However this code is also quite old (2011) so it might still be from a time where there was a different idea about this.

@michele-scandale
Copy link
Contributor Author

Anything we do to make this work better risks being interpreted as treating this as somehow supported, which it absolutely is not.

Alright. I guess I'll close this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:codegen IR generation bugs: mangling, exceptions, etc. clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants