Skip to content

Commit 10a55ca

Browse files
authored
[RISCV] Support constraint "s" (#80201)
GCC has supported a generic constraint "s" for a long time (since at least 1992), which references a symbol or label with an optional constant offset. "i" is a superset that also supports a constant integer. GCC's RISC-V port also supports a machine-specific constraint "S", which cannot be used with a preemptible symbol. (We don't bother to check preemptibility.) In PIC code, an external symbol is preemptible by default, making "S" less useful if you want to create an artificial reference for linker garbage collection, or define sections to hold symbol addresses: ``` void fun(); // error: impossible constraint in ‘asm’ for riscv64-linux-gnu-gcc -fpie/-fpic void foo() { asm(".reloc ., BFD_RELOC_NONE, %0" :: "S"(fun)); } // good even if -fpie/-fpic void foo() { asm(".reloc ., BFD_RELOC_NONE, %0" :: "s"(fun)); } ``` This patch adds support for "s". Modify https://reviews.llvm.org/D105254 ("S") to handle multi-depth GEPs (https://reviews.llvm.org/D61560).
1 parent 6a3fde6 commit 10a55ca

File tree

8 files changed

+110
-67
lines changed

8 files changed

+110
-67
lines changed

clang/lib/Basic/Targets/RISCV.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,8 @@ bool RISCVTargetInfo::validateAsmConstraint(
9696
// An address that is held in a general-purpose register.
9797
Info.setAllowsMemory();
9898
return true;
99-
case 'S': // A symbolic address
99+
case 's':
100+
case 'S': // A symbol or label reference with a constant offset
100101
Info.setAllowsRegister();
101102
return true;
102103
case 'v':

clang/test/CodeGen/RISCV/riscv-inline-asm.c

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,16 @@ void test_A(int *p) {
4545
asm volatile("" :: "A"(*p));
4646
}
4747

48-
void test_S(void) {
49-
// CHECK-LABEL: define{{.*}} void @test_S()
50-
// CHECK: call void asm sideeffect "", "S"(ptr nonnull @f)
51-
asm volatile("" :: "S"(&f));
48+
extern int var, arr[2][2];
49+
struct Pair { int a, b; } pair;
50+
51+
// CHECK-LABEL: test_s(
52+
// CHECK: call void asm sideeffect "// $0 $1 $2", "s,s,s"(ptr nonnull @var, ptr nonnull getelementptr inbounds ([2 x [2 x i32]], ptr @arr, {{.*}}), ptr nonnull @test_s)
53+
// CHECK: call void asm sideeffect "// $0", "s"(ptr nonnull getelementptr inbounds (%struct.Pair, ptr @pair, {{.*}}))
54+
// CHECK: call void asm sideeffect "// $0 $1 $2", "S,S,S"(ptr nonnull @var, ptr nonnull getelementptr inbounds ([2 x [2 x i32]], ptr @arr, {{.*}}), ptr nonnull @test_s)
55+
void test_s(void) {
56+
asm("// %0 %1 %2" :: "s"(&var), "s"(&arr[1][1]), "s"(test_s));
57+
asm("// %0" :: "s"(&pair.b));
58+
59+
asm("// %0 %1 %2" :: "S"(&var), "S"(&arr[1][1]), "S"(test_s));
5260
}

clang/test/Sema/inline-asm-validate-riscv.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,14 @@ void K(int k) {
2222
asm volatile ("" :: "K"(AboveMax)); // expected-error{{value '32' out of range for constraint 'K'}}
2323
}
2424

25+
void test_s(int i) {
26+
asm("" :: "s"(test_s(0))); // expected-error{{invalid type 'void' in asm input for constraint 's'}}
27+
/// Codegen error
28+
asm("" :: "s"(i));
29+
30+
asm("" :: "S"(test_s(0))); // expected-error{{invalid type 'void' in asm input for constraint 'S'}}
31+
}
32+
2533
void test_clobber_conflict(void) {
2634
register long x10 asm("x10");
2735
asm volatile("" :: "r"(x10) : "x10"); // expected-error {{conflicts with asm clobber list}}

llvm/docs/LangRef.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5075,7 +5075,7 @@ Some constraint codes are typically supported by all targets:
50755075
- ``i``: An integer constant (of target-specific width). Allows either a simple
50765076
immediate, or a relocatable value.
50775077
- ``n``: An integer constant -- *not* including relocatable values.
5078-
- ``s``: An integer constant, but allowing *only* relocatable values.
5078+
- ``s``: A symbol or label reference with a constant offset.
50795079
- ``X``: Allows an operand of any kind, no constraint whatsoever. Typically
50805080
useful to pass a label for an asm branch or call.
50815081

@@ -5283,6 +5283,7 @@ RISC-V:
52835283
- ``f``: A 32- or 64-bit floating-point register (requires F or D extension).
52845284
- ``r``: A 32- or 64-bit general-purpose register (depending on the platform
52855285
``XLEN``).
5286+
- ``S``: Alias for ``s``.
52865287
- ``vr``: A vector register. (requires V extension).
52875288
- ``vm``: A vector register for masking operand. (requires V extension).
52885289

llvm/lib/Target/RISCV/RISCVISelLowering.cpp

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19188,6 +19188,7 @@ RISCVTargetLowering::getConstraintType(StringRef Constraint) const {
1918819188
return C_Immediate;
1918919189
case 'A':
1919019190
return C_Memory;
19191+
case 's':
1919119192
case 'S': // A symbolic address
1919219193
return C_Other;
1919319194
}
@@ -19449,13 +19450,7 @@ void RISCVTargetLowering::LowerAsmOperandForConstraint(
1944919450
}
1945019451
return;
1945119452
case 'S':
19452-
if (const auto *GA = dyn_cast<GlobalAddressSDNode>(Op)) {
19453-
Ops.push_back(DAG.getTargetGlobalAddress(GA->getGlobal(), SDLoc(Op),
19454-
GA->getValueType(0)));
19455-
} else if (const auto *BA = dyn_cast<BlockAddressSDNode>(Op)) {
19456-
Ops.push_back(DAG.getTargetBlockAddress(BA->getBlockAddress(),
19457-
BA->getValueType(0)));
19458-
}
19453+
TargetLowering::LowerAsmOperandForConstraint(Op, "s", Ops, DAG);
1945919454
return;
1946019455
default:
1946119456
break;

llvm/test/CodeGen/RISCV/inline-asm-S-constraint.ll

Lines changed: 0 additions & 54 deletions
This file was deleted.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
; RUN: not llc -mtriple=riscv64 < %s 2>&1 | FileCheck %s
2+
3+
@a = external global [4 x i32], align 16
4+
5+
; CHECK-COUNT-2: error: invalid operand for inline asm constraint 's'
6+
; CHECK-NOT: error:
7+
define void @test(i64 %i) {
8+
entry:
9+
%x = alloca i32, align 4
10+
%ai = getelementptr inbounds [4 x i32], ptr @a, i64 0, i64 %i
11+
call void asm sideeffect "", "s,~{dirflag},~{fpsr},~{flags}"(ptr %x)
12+
call void asm sideeffect "", "s,~{dirflag},~{fpsr},~{flags}"(ptr %ai)
13+
ret void
14+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
2+
; RUN: llc -mtriple=riscv32 -relocation-model=static < %s | FileCheck %s --check-prefix=RV32
3+
; RUN: llc -mtriple=riscv64 -relocation-model=pic < %s | FileCheck %s --check-prefix=RV64
4+
5+
@var = external dso_local global i32, align 4
6+
@a = external global [2 x [2 x i32]], align 4
7+
8+
define dso_local void @test() {
9+
; RV32-LABEL: test:
10+
; RV32: # %bb.0: # %entry
11+
; RV32-NEXT: #APP
12+
; RV32-NEXT: # var a+12 test
13+
; RV32-NEXT: #NO_APP
14+
; RV32-NEXT: #APP
15+
; RV32-NEXT: # var a+12 test
16+
; RV32-NEXT: #NO_APP
17+
; RV32-NEXT: ret
18+
;
19+
; RV64-LABEL: test:
20+
; RV64: # %bb.0: # %entry
21+
; RV64-NEXT: #APP
22+
; RV64-NEXT: # var a+12 .Ltest$local
23+
; RV64-NEXT: #NO_APP
24+
; RV64-NEXT: #APP
25+
; RV64-NEXT: # var a+12 .Ltest$local
26+
; RV64-NEXT: #NO_APP
27+
; RV64-NEXT: ret
28+
entry:
29+
call void asm sideeffect "// $0 $1 $2", "s,s,s,~{dirflag},~{fpsr},~{flags}"(ptr @var, ptr getelementptr inbounds ([2 x [2 x i32]], ptr @a, i64 0, i64 1, i64 1), ptr @test)
30+
31+
;; Implement "S" as an alias for "s".
32+
call void asm sideeffect "// $0 $1 $2", "S,S,S,~{dirflag},~{fpsr},~{flags}"(ptr @var, ptr getelementptr inbounds ([2 x [2 x i32]], ptr @a, i64 0, i64 1, i64 1), ptr @test)
33+
ret void
34+
}
35+
36+
; Function Attrs: nofree nosync nounwind readnone
37+
define dso_local ptr @test_label() {
38+
; RV32-LABEL: test_label:
39+
; RV32: # %bb.0: # %entry
40+
; RV32-NEXT: .Ltmp0: # Block address taken
41+
; RV32-NEXT: # %bb.1: # %L1
42+
; RV32-NEXT: #APP
43+
; RV32-NEXT: # .Ltmp0
44+
; RV32-NEXT: #NO_APP
45+
; RV32-NEXT: #APP
46+
; RV32-NEXT: lui a0, %hi(.Ltmp0)
47+
; RV32-NEXT: addi a0, a0, %lo(.Ltmp0)
48+
; RV32-NEXT: #NO_APP
49+
; RV32-NEXT: ret
50+
;
51+
; RV64-LABEL: test_label:
52+
; RV64: # %bb.0: # %entry
53+
; RV64-NEXT: .Ltmp0: # Block address taken
54+
; RV64-NEXT: # %bb.1: # %L1
55+
; RV64-NEXT: #APP
56+
; RV64-NEXT: # .Ltmp0
57+
; RV64-NEXT: #NO_APP
58+
; RV64-NEXT: #APP
59+
; RV64-NEXT: lui a0, %hi(.Ltmp0)
60+
; RV64-NEXT: addi a0, a0, %lo(.Ltmp0)
61+
; RV64-NEXT: #NO_APP
62+
; RV64-NEXT: ret
63+
entry:
64+
br label %L1
65+
66+
L1:
67+
call void asm sideeffect "// $0", "s,~{dirflag},~{fpsr},~{flags}"(ptr blockaddress(@test_label, %L1))
68+
%ret = tail call ptr asm "lui $0, %hi($1)\0Aaddi $0, $0, %lo($1)", "=r,S"(ptr blockaddress(@test_label, %L1))
69+
ret ptr %ret
70+
}

0 commit comments

Comments
 (0)