Files
Nikita Popov fcfc9167b6 Reapply [IR] Explicitly specify target feature for module asm (#204548) (#207677)
Reapply with a use-after-free fix. The MC subtarget used for inline
assembly has to stay alive until finalization, not only during the
emitInlineAsm() call.

-----

Support specifying additional properties on module-level inline
assembly. In particular, the target features and target CPU can now be
specified as follows:

    module asm(target_features: "+foo", target_cpu: "bar")
        "asm line 1"
        "asm line 2"

There may be multiple module inline assembly blocks with different
properties.

This is intended to fix the long standing issue where in LTO scenarios
we don't know what target features to use for parsing the module-level
inline assembly. Now they can be faithfully preserved, even when merging
inline assembly from different modules with different features.

If target_features and target_cpu are empty, we fall back to the old
behavior (which, in the backend, would be to use the default subtarget
of the TargetMachine).

Fixes https://github.com/llvm/llvm-project/issues/61991.
Fixes https://github.com/llvm/llvm-project/issues/67698.
2026-07-06 11:57:38 +02:00

60 lines
1.9 KiB
C++

// REQUIRES: x86-registered-target
// RUN: %clang_cc1 -triple x86_64 %s -S -o /dev/null -Werror -verify
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm %s -o - | FileCheck %s
struct string_view {
int S;
const char* D;
constexpr string_view() : S(0), D(0){}
constexpr string_view(const char* Str) : S(__builtin_strlen(Str)), D(Str) {}
constexpr string_view(int Size, const char* Str) : S(Size), D(Str) {}
constexpr int size() const {
return S;
}
constexpr const char* data() const {
return D;
}
};
namespace GH143242 {
constexpr string_view code2 = R"(nop; nop; nop; nop)";
asm((code2));
// CHECK: module asm(target_features: "{{.*}}")
// CHECK-NEXT: "nop; nop; nop; nop"
}
int func() {return 0;};
void f() {
asm((string_view("")) ::(string_view("r"))(func()));
// CHECK: %[[CALL:.*]] = call noundef i32 @_Z4funcv
// CHECK: call void asm sideeffect "", "r,~{dirflag},~{fpsr},~{flags}"
asm("" :::(string_view("memory")));
// CHECK: call void asm sideeffect "", "~{memory},~{dirflag},~{fpsr},~{flags}"
}
void foo(unsigned long long addr, unsigned long long a0) {
register unsigned long long result asm("rax");
register unsigned long long b0 asm("rdi");
b0 = a0;
asm((string_view("call *%1")) : (string_view("=r")) (result)
: (string_view("r"))(addr), (string_view("r")) (b0) : (string_view("memory")));
// CHECK:{{.*}} call i64 asm "call *$1", "={rax},r,{rdi},~{memory},~{dirflag},~{fpsr},~{flags}"
}
void test_srcloc() {
asm((string_view( // expected-error {{invalid instruction mnemonic 'nonsense'}} \
// expected-error {{invalid instruction mnemonic 'foobar'}} \
// expected-note@1 {{instantiated into assembly here}} \
// expected-note@2 {{instantiated into assembly here}}
R"o(nonsense
foobar)o")
) ::(string_view("r"))(func()));
}