Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
Min-Yih “Min” Hsu @ LLVM Dev Meeting 2021
Handling inline assembly 

in Clang and LLVM
about:me
“Min” Hsu
• Computer Science PhD Candidate in
University of California, Irvine

• Code owner of M68k LLVM backend

• Author of book “LLVM Techniques,
Tips and Best Practices” (2021)
2
How Inline Assembly is
Processed in Clang & LLVM
3
Inline Assembly
4
Introduction to inline assembly
5
void foo(...) {


int sum = 0;


bool flag = ...;


if (flag)


asm ("movl %%eax, %%ebxn"


"addl %%ebx, %%esi" :::);


else


sum += 87;


}


* x86_64 assembly w/ AT&T syntax
Introduction to inline assembly
5
void foo(...) {


int sum = 0;


bool flag = ...;


if (flag)


asm ("movl %%eax, %%ebxn"


"addl %%ebx, %%esi" :::);


else


sum += 87;


}


* x86_64 assembly w/ AT&T syntax
Introduction to inline assembly
5
void foo(...) {


int sum = 0;


bool flag = ...;


if (flag)


asm ("movl %%eax, %%ebxn"


"addl %%ebx, %%esi" :::);


else


sum += 87;


}


foo:


pushq %rbp


movq %rsp, %rbp


...


testb $1, -9(%rbp)


je LBB0_2


## InlineAsm Start


movl %eax, %ebx


addl %ebx, %esi


## InlineAsm End


jmp LBB0_3


LBB0_2:


movl -8(%rbp), %eax


addl $87, %eax


movl %eax, -8(%rbp)


LBB0_3:


popq %rbp


retq
* x86_64 assembly w/ AT&T syntax
Introduction to inline assembly
5
void foo(...) {


int sum = 0;


bool flag = ...;


if (flag)


asm ("movl %%eax, %%ebxn"


"addl %%ebx, %%esi" :::);


else


sum += 87;


}


foo:


pushq %rbp


movq %rsp, %rbp


...


testb $1, -9(%rbp)


je LBB0_2


## InlineAsm Start


movl %eax, %ebx


addl %ebx, %esi


## InlineAsm End


jmp LBB0_3


LBB0_2:


movl -8(%rbp), %eax


addl $87, %eax


movl %eax, -8(%rbp)


LBB0_3:


popq %rbp


retq
* x86_64 assembly w/ AT&T syntax
Introduction to inline assembly
Why use inline assembly?
6
void foo(...) {


int sum = 0;


bool flag = ...;


if (flag)


asm ("movl %%eax, %%ebxn"


"addl %%ebx, %%esi" :::);


else


sum += 87;


}


* x86_64 assembly w/ AT&T syntax
Introduction to inline assembly
Why use inline assembly?
6
void foo(...) {


int sum = 0;


bool flag = ...;


if (flag)


asm ("movl %%eax, %%ebxn"


"addl %%ebx, %%esi" :::);


else


sum += 87;


}


Performance critical code
* x86_64 assembly w/ AT&T syntax
Introduction to inline assembly
Why use inline assembly?
6
void foo(...) {


int sum = 0;


bool flag = ...;


if (flag)


asm ("movl %%eax, %%ebxn"


"addl %%ebx, %%esi" :::);


else


sum += 87;


}


Performance critical code
Low-level code

e.g. Kernel, firmware
* x86_64 assembly w/ AT&T syntax
Introduction to inline assembly
Why use inline assembly?
6
void foo(...) {


int sum = 0;


bool flag = ...;


if (flag)


asm ("movl %%eax, %%ebxn"


"addl %%ebx, %%esi" :::);


else


sum += 87;


}


Performance critical code
Low-level code

e.g. Kernel, firmware
Compiler optimizations “barrier”
* x86_64 assembly w/ AT&T syntax
Introduction to inline assembly
7
asm (“movl %%eax, %%ebxn"


"addl %%ebx, %%esi" :::);
* x86_64 assembly w/ AT&T syntax
Introduction to inline assembly
7
asm (“movl %%eax, %%ebxn"


"addl %%ebx, %%esi" :::);
Assembly code (template)
* x86_64 assembly w/ AT&T syntax
Introduction to inline assembly
7
asm (“movl %%eax, %%ebxn"


"addl %%ebx, %%esi" :::);
Assembly code (template)
* x86_64 assembly w/ AT&T syntax
Introduction to inline assembly
Output operands
8
int out_var;


asm ("movl %%eax, %%ebxn"


"addl %%ebx, %0"


: "=r"(out_var) ::);
* x86_64 assembly w/ AT&T syntax
Introduction to inline assembly
Output operands
8
int out_var;


asm ("movl %%eax, %%ebxn"


"addl %%ebx, %0"


: "=r"(out_var) ::);
* x86_64 assembly w/ AT&T syntax
Introduction to inline assembly
Output operands
8
int out_var;


asm ("movl %%eax, %%ebxn"


"addl %%ebx, %0"


: "=r"(out_var) ::);
Operand constraints
* x86_64 assembly w/ AT&T syntax
Introduction to inline assembly
Operands constraints
9
int out_var;


asm ("movl %%eax, %%ebxn"


"addl %%ebx, %0"


: "=r"(out_var) ::);
* x86_64 assembly w/ AT&T syntax
Introduction to inline assembly
Operands constraints
9
int out_var;


asm ("movl %%eax, %%ebxn"


"addl %%ebx, %0"


: "=r"(out_var) ::);
* x86_64 assembly w/ AT&T syntax
## InlineAsm Start


movl %eax, %ebx


addl %ebx, %esi


## InlineAsm End
Introduction to inline assembly
Operands constraints
9
int out_var;


asm ("movl %%eax, %%ebxn"


"addl %%ebx, %0"


: "=r"(out_var) ::);
* x86_64 assembly w/ AT&T syntax
## InlineAsm Start


movl %eax, %ebx


addl %ebx, %esi


## InlineAsm End
## InlineAsm Start


movl %eax, %ebx


addl %ebx, -8(%ebp)


## InlineAsm End
Operand constraints
10
Operand constraints
10
• ‘r’ : General-purpose register operand

• ‘i’ : Immediate integer operand

• ‘m’ : Memory operand w/ arbitrary addressing mode
Target-independent Constraints
Operand constraints
10
• ‘r’ : General-purpose register operand

• ‘i’ : Immediate integer operand

• ‘m’ : Memory operand w/ arbitrary addressing mode
Target-independent Constraints
• ‘a’ : AL / AH / EAX / RAX

• ‘I’ : Integer constant in the range of [0, 31]

• ‘N’ : Unsigned 8-bit integer constant
X86 Constraints
Operand constraints
10
• ‘r’ : General-purpose register operand

• ‘i’ : Immediate integer operand

• ‘m’ : Memory operand w/ arbitrary addressing mode
Target-independent Constraints
• ‘a’ : AL / AH / EAX / RAX

• ‘I’ : Integer constant in the range of [0, 31]

• ‘N’ : Unsigned 8-bit integer constant
X86 Constraints
• ‘J’ : 16-bit signed integer constant

• “Ci” : Constant integers

• “Cj” : Constant signed integers that do NOT
fi
t in 16 bits
M68k Constraints
Operand constraints
10
• ‘r’ : General-purpose register operand

• ‘i’ : Immediate integer operand

• ‘m’ : Memory operand w/ arbitrary addressing mode
Constraint Modifers
Target-independent Constraints
• ‘=‘ : This is an output operand

• ‘+’ : This is an input / output operand
• ‘a’ : AL / AH / EAX / RAX

• ‘I’ : Integer constant in the range of [0, 31]

• ‘N’ : Unsigned 8-bit integer constant
X86 Constraints
• ‘J’ : 16-bit signed integer constant

• “Ci” : Constant integers

• “Cj” : Constant signed integers that do NOT
fi
t in 16 bits
M68k Constraints
Introduction to inline assembly
Input operands
11
int out_var, in_var;


asm ("movl %1, %%ebxn"


"addl %%ebx, %0"


: “=r"(out_var)


: “r”(in_var) :);
* x86_64 assembly w/ AT&T syntax
Introduction to inline assembly
Input operands
11
int out_var, in_var;


asm ("movl %1, %%ebxn"


"addl %%ebx, %0"


: “=r"(out_var)


: “r”(in_var) :);
* x86_64 assembly w/ AT&T syntax
Introduction to inline assembly
Input operands
11
int out_var, in_var;


asm ("movl %1, %%ebxn"


"addl %%ebx, %0"


: “=r"(out_var)


: “r”(in_var) :);
Operand constraints
* x86_64 assembly w/ AT&T syntax
Introduction to inline assembly
Clobber operands
12
int out_var, in_var;


asm ("movl %1, %%ebxn"


"addl %%ebx, %0"


: “=r"(out_var)


: “r”(in_var)


: “ebx”);
* x86_64 assembly w/ AT&T syntax
Introduction to inline assembly
Clobber operands
12
int out_var, in_var;


asm ("movl %1, %%ebxn"


"addl %%ebx, %0"


: “=r"(out_var)


: “r”(in_var)


: “ebx”);
* x86_64 assembly w/ AT&T syntax
For more inline assembly syntax…
13
https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
Handling inline assembly in Clang & LLVM
Background
14
Handling inline assembly in Clang & LLVM
Background
• Most parts of an inline assembly string are simply copied into the
fi
nal
assembly
fi
le
14
Handling inline assembly in Clang & LLVM
Background
• Most parts of an inline assembly string are simply copied into the
fi
nal
assembly
fi
le
• LLVM needs to “glue” inline assembly operands with the surrounding code
14
Handling inline assembly in Clang & LLVM
Background
• Most parts of an inline assembly string are simply copied into the
fi
nal
assembly
fi
le
• LLVM needs to “glue” inline assembly operands with the surrounding code
• Lots of target-speci
fi
c logics

• In both Clang and the backend
14
Handling inline assembly in Clang & LLVM
Background
• Most parts of an inline assembly string are simply copied into the
fi
nal
assembly
fi
le
• LLVM needs to “glue” inline assembly operands with the surrounding code
• Lots of target-speci
fi
c logics

• In both Clang and the backend
• Target-speci
fi
c callbacks are scattered in the codebase

• Documentation for this part is a little…shy
14
Goals
15
Goals
Learning inline assembly workflow in Clang / LLVM
15
Goals
Learning inline assembly workflow in Clang / LLVM
A simple guide for backend developers to add inline assembly support
15
Outline of target-specific logics in each stage
16
Clang
Lowering
LLVM IR
Machine IR
AsmPrinter
Outline of target-specific logics in each stage
16
Clang
Lowering
LLVM IR
Machine IR
AsmPrinter
• Simple validation on operand constraints

• Converting constraints
Outline of target-specific logics in each stage
16
Clang
Lowering
LLVM IR
Machine IR
AsmPrinter
• Simple validation on operand constraints

• Converting constraints
• Classifying constraints

• Constraint validations

• Lowering the operands
Outline of target-specific logics in each stage
16
Clang
Lowering
LLVM IR
Machine IR
AsmPrinter
• Simple validation on operand constraints

• Converting constraints
• Classifying constraints

• Constraint validations

• Lowering the operands
Print out di
ff
erent types of operands
Outline of target-specific logics in each stage
17
Clang
Lowering
LLVM IR
Machine IR
AsmPrinter
• Simple validation on operand constraints

• Converting constraints
• Classifying constraints

• Constraint validations

• Lowering the operands
Print out di
ff
erent types of operands
Operand constraint validations in Clang
18
bool TargetInfo::validateAsmConstraint(const char *&, ConstraintInfo &) const;
Operand constraint validations in Clang
18
bool TargetInfo::validateAsmConstraint(const char *&, ConstraintInfo &) const;
bool M68kTargetInfo::validateAsmConstraint(const char *&Name, ConstraintInfo &info) const {


switch (*Name) {


case 'a': // address register


info.setAllowsRegister();


return true;


}


…


}
Operand constraint validations in Clang
19
bool TargetInfo::validateAsmConstraint(const char *&, ConstraintInfo &) const;
bool M68kTargetInfo::validateAsmConstraint(const char *&Name, ConstraintInfo &info) const {


switch (*Name) {


case 'a': // address register


info.setAllowsRegister();


return true;


case 'J': // constant signed 16-bit integer


info.setRequiresImmediate(std::numeric_limits<int16_t>::min(),


std::numeric_limits<int16_t>::max());


return true;


}


…


}
Operand constraint validations in Clang
Limitation on immediate value validations
20
void foo() {


int32_t x;


asm ("move.l %0, %%d1" : : "J" (x));


}
* M68k assembly w/ Motorola syntax
Operand constraint validations in Clang
Limitation on immediate value validations
20
void foo() {


int32_t x;


asm ("move.l %0, %%d1" : : "J" (x));


}
Constant signed 16-bit integer
* M68k assembly w/ Motorola syntax
Operand constraint validations in Clang
Limitation on immediate value validations
20
void foo() {


int32_t x;


asm ("move.l %0, %%d1" : : "J" (x));


}
$ clang -target m68k -fsyntax-only foo.c


# No error


$ clang -target m68k -emit-llvm foo.c


# No error
Constant signed 16-bit integer
* M68k assembly w/ Motorola syntax
Operand constraint validations in Clang
Limitation on immediate value validations
20
void foo() {


int32_t x;


asm ("move.l %0, %%d1" : : "J" (x));


}
$ clang -target m68k -fsyntax-only foo.c


# No error


$ clang -target m68k -emit-llvm foo.c


# No error
Constant signed 16-bit integer
$ clang -target m68k -S foo.c


error: constraint 'J' expects an integer constant expression
* M68k assembly w/ Motorola syntax
Inline assembly in LLVM IR
21
void foo() {


const int x = 87;


asm ("move.l %0, %%d1" : : "Ci" (x) : "d1");


}
C/C++
* M68k assembly w/ Motorola syntax
Inline assembly in LLVM IR
21
void foo() {


const int x = 87;


asm ("move.l %0, %%d1" : : "Ci" (x) : "d1");


}
C/C++
call void asm sideeffect "move.l $0, %d1", “^Ci,~{d1}“(i32 87)
LLVM IR
* M68k assembly w/ Motorola syntax
Inline assembly in LLVM IR
21
void foo() {


const int x = 87;


asm ("move.l %0, %%d1" : : "Ci" (x) : "d1");


}
C/C++
call void asm sideeffect "move.l $0, %d1", “^Ci,~{d1}“(i32 87)
LLVM IR
* M68k assembly w/ Motorola syntax
Inline assembly in LLVM IR
21
void foo() {


const int x = 87;


asm ("move.l %0, %%d1" : : "Ci" (x) : "d1");


}
C/C++
call void asm sideeffect "move.l $0, %d1", “^Ci,~{d1}“(i32 87)
LLVM IR
* M68k assembly w/ Motorola syntax
Inline assembly in LLVM IR
21
void foo() {


const int x = 87;


asm ("move.l %0, %%d1" : : "Ci" (x) : "d1");


}
C/C++
call void asm sideeffect "move.l $0, %d1", “^Ci,~{d1}“(i32 87)
LLVM IR
* M68k assembly w/ Motorola syntax
Inline assembly in LLVM IR
21
void foo() {


const int x = 87;


asm ("move.l %0, %%d1" : : "Ci" (x) : "d1");


}
C/C++
call void asm sideeffect "move.l $0, %d1", “^Ci,~{d1}“(i32 87)
LLVM IR
* M68k assembly w/ Motorola syntax
Outline of target-specific logics in each stage
22
Clang
Lowering
LLVM IR
Machine IR
AsmPrinter
• Simple validation on operand constraints

• Converting constraints
• Classifying constraints

• Constraint validations

• Lowering the operands
Print out di
ff
erent types of operands
23
LLVM IR call void asm sideeffect "move.l $0, %d1", “^Ci,~{d1}“(i32 87)
23
LLVM IR
SelectionDAG
call void asm sideeffect "move.l $0, %d1", “^Ci,~{d1}“(i32 87)
t2: ch,glue = inlineasm ... "move.l $0, %d1", ...,


TargetConstant:i32<87>, Register:i16 $d1
23
LLVM IR
SelectionDAG
Machine IR
call void asm sideeffect "move.l $0, %d1", “^Ci,~{d1}“(i32 87)
INLINEASM &"move.l $0, %d1", ..., /* imm */ 87, /* clobber */ $d1
t2: ch,glue = inlineasm ... "move.l $0, %d1", ...,


TargetConstant:i32<87>, Register:i16 $d1
23
LLVM IR
SelectionDAG
Machine IR
call void asm sideeffect "move.l $0, %d1", “^Ci,~{d1}“(i32 87)
INLINEASM &"move.l $0, %d1", ..., /* imm */ 87, /* clobber */ $d1
t2: ch,glue = inlineasm ... "move.l $0, %d1", ...,


TargetConstant:i32<87>, Register:i16 $d1
Constraint classification
24
TargetLowering::ConstraintType


M68kTargetLowering::getConstraintType(StringRef Constraint) const;
Constraint classification
24
TargetLowering::ConstraintType


M68kTargetLowering::getConstraintType(StringRef Constraint) const;
Return:
Constraint classification
24
TargetLowering::ConstraintType


M68kTargetLowering::getConstraintType(StringRef Constraint) const;
• C_RegisterClass
Return:
Ex: ’r’
Constraint classification
24
TargetLowering::ConstraintType


M68kTargetLowering::getConstraintType(StringRef Constraint) const;
• C_RegisterClass
• C_Immediate
Return:
Ex: ’r’
Ex: ‘i’
Constraint classification
24
TargetLowering::ConstraintType


M68kTargetLowering::getConstraintType(StringRef Constraint) const;
• C_RegisterClass
• C_Immediate
• C_Memory
Return:
Ex: ’r’
Ex: ‘i’
Ex: ‘m’, ‘Q’ (AArch64)
Constraint classification
24
TargetLowering::ConstraintType


M68kTargetLowering::getConstraintType(StringRef Constraint) const;
• C_RegisterClass
• C_Immediate
• C_Memory
• C_Other
Return:
Ex: ’r’
Ex: ‘i’
Ex: ‘m’, ‘Q’ (AArch64)
Lowering operands
25
getConstraintType
Method in XXXTargetLowering
Method in XXXISelDAGToDAG
Will be invoked
Lowering operands
25
getConstraintType
getRegForInlineAsmConstraint
C_RegisterClass
Method in XXXTargetLowering
Method in XXXISelDAGToDAG
Will be invoked
Lowering operands
25
getConstraintType
getRegForInlineAsmConstraint
LowerAsmOperandForConstraint
C_RegisterClass
C_Immediate / C_Other
Method in XXXTargetLowering
Method in XXXISelDAGToDAG
Will be invoked
Lowering operands
25
getConstraintType
getRegForInlineAsmConstraint
getInlineAsmMemConstraint
LowerAsmOperandForConstraint
C_RegisterClass
C_Immediate / C_Other
C_Memory
Method in XXXTargetLowering
Method in XXXISelDAGToDAG
Will be invoked
Lowering operands
25
getConstraintType
getRegForInlineAsmConstraint
getInlineAsmMemConstraint
LowerAsmOperandForConstraint
SelectInlineAsmMemoryOperand
C_RegisterClass
C_Immediate / C_Other
C_Memory
Method in XXXTargetLowering
Method in XXXISelDAGToDAG
Will be invoked
Lowering register operands
26
std::pair<unsigned, const TargetRegisterClass *>


TargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,


StringRef Constraint,


MVT VT) const;
Lowering register operands
26
std::pair<unsigned, const TargetRegisterClass *>


TargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,


StringRef Constraint,


MVT VT) const;
A speci
fi
c register or 0 if not applicable
Lowering register operands
26
std::pair<unsigned, const TargetRegisterClass *>


TargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,


StringRef Constraint,


MVT VT) const;
A speci
fi
c register or 0 if not applicable Valid register class to select from
Lowering register operands
M68k Example
27
std::pair<unsigned, const TargetRegisterClass *>


M68kTargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *,


StringRef Constraint,


MVT VT) const {


switch (Constraint[0]) {


case 'a':


switch (VT.SimpleTy) {


case MVT::i16:


return std::make_pair(0U, &M68k::AR16RegClass);


}


}


}
Lowering register operands
M68k Example
27
std::pair<unsigned, const TargetRegisterClass *>


M68kTargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *,


StringRef Constraint,


MVT VT) const {


switch (Constraint[0]) {


case 'a':


switch (VT.SimpleTy) {


case MVT::i16:


return std::make_pair(0U, &M68k::AR16RegClass);


}


}


}
Lowering register operands
X86 Example
28
std::pair<unsigned, const TargetRegisterClass *>


X86TargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *,


StringRef Constraint,


MVT VT) const {


...


if (Constraint == "Yz") {


// First SSE register (%xmm0).


switch (VT.SimpleTy) {


case MVT::f32:


case MVT::i32:


return std::make_pair(X86::XMM0, &X86::FR32RegClass);


}


}


}
Lowering register operands
X86 Example
28
std::pair<unsigned, const TargetRegisterClass *>


X86TargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *,


StringRef Constraint,


MVT VT) const {


...


if (Constraint == "Yz") {


// First SSE register (%xmm0).


switch (VT.SimpleTy) {


case MVT::f32:


case MVT::i32:


return std::make_pair(X86::XMM0, &X86::FR32RegClass);


}


}


}
Lowering immediate / other operands
29
void TargetLowering::


LowerAsmOperandForConstraint(SDValue Op,


std::string &Constraint,


std::vector<SDValue> &Ops,


SelectionDAG &DAG) const;
Lowering immediate / other operands
M68k Example
30
void M68kTargetLowering::LowerAsmOperandForConstraint(SDValue Op,


std::string &Constraint,


std::vector<SDValue> &Ops,


SelectionDAG &DAG) const {


switch (Constraint[0]) {


case 'J': { // constant signed 16-bit integer


}


}


}
Lowering immediate / other operands
M68k Example
31
void M68kTargetLowering::LowerAsmOperandForConstraint(SDValue Op,


std::string &Constraint,


std::vector<SDValue> &Ops,


SelectionDAG &DAG) const {


switch (Constraint[0]) {


case 'J': { // constant signed 16-bit integer


if (auto *C = dyn_cast<ConstantSDNode>(Op)) {


int64_t Val = C->getSExtValue();


}


}


}


}
Lowering immediate / other operands
M68k Example
32
void M68kTargetLowering::LowerAsmOperandForConstraint(SDValue Op,


std::string &Constraint,


std::vector<SDValue> &Ops,


SelectionDAG &DAG) const {


switch (Constraint[0]) {


case 'J': { // constant signed 16-bit integer


if (auto *C = dyn_cast<ConstantSDNode>(Op)) {


int64_t Val = C->getSExtValue();


if (isInt<16>(Val)) {


Ops.push_back(Op);


return;


}


}


}


}


}
Lowering memory operands
Memory constraint classi
fi
cation
33
unsigned TargetLowering::getInlineAsmMemConstraint(StringRef ConstraintCode) const;
Lowering memory operands
Memory constraint classi
fi
cation
33
unsigned TargetLowering::getInlineAsmMemConstraint(StringRef ConstraintCode) const;
Return:
Lowering memory operands
Memory constraint classi
fi
cation
33
unsigned TargetLowering::getInlineAsmMemConstraint(StringRef ConstraintCode) const;
• InlineAsm::Constraint_m


• InlineAsm::Constraint_o


• InlineAsm::Constraint_v
Return:
Generic memory constraints
Lowering memory operands
Memory constraint classi
fi
cation
33
unsigned TargetLowering::getInlineAsmMemConstraint(StringRef ConstraintCode) const;
• InlineAsm::Constraint_m


• InlineAsm::Constraint_o


• InlineAsm::Constraint_v
Return:
• InlineAsm::Constraint_A


• InlineAsm::Constraint_Q
Generic memory constraints
Target-speci
fi
c constraints!
Lowering memory operands
Memory constraint classi
fi
cation — RISCV Example
34
unsigned RISCVTargetLowering::


getInlineAsmMemConstraint(StringRef ConstraintCode) const {


switch (ConstraintCode[0]) {


case 'A':


return InlineAsm::Constraint_A;


...


}


...


}
Lowering memory operands
35
bool


SelectionDAGISel::SelectInlineAsmMemoryOperand(const SDValue &Op,


unsigned ConstraintID,


std::vector<SDValue> &OutOps);
Lowering memory operands
RISCV Example
36
bool RISCVDAGToDAGISel::SelectInlineAsmMemoryOperand(


const SDValue &Op, unsigned ConstraintID,


std::vector<SDValue> &OutOps) {


switch (ConstraintID) {


case InlineAsm::Constraint_A:


OutOps.push_back(Op);


return false;


default:


break;


}




return true;


}
Lowering memory operands
Complex addressing mode — X86 Example
37
bool X86DAGToDAGISel::


SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID,


std::vector<SDValue> &OutOps) {


SDValue Op0, Op1, Op2, Op3, Op4;




switch (ConstraintID) {


case InlineAsm::Constraint_m: // memory


if (!selectAddr(nullptr, Op, Op0, Op1, Op2, Op3, Op4))


return true;


break;


}


OutOps.insert(OutOps.end(), {Op0, Op1, Op2, Op3, Op4});


return false;


}
Lowering memory operands
Complex addressing mode — X86 Example
37
bool X86DAGToDAGISel::


SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID,


std::vector<SDValue> &OutOps) {


SDValue Op0, Op1, Op2, Op3, Op4;




switch (ConstraintID) {


case InlineAsm::Constraint_m: // memory


if (!selectAddr(nullptr, Op, Op0, Op1, Op2, Op3, Op4))


return true;


break;


}


OutOps.insert(OutOps.end(), {Op0, Op1, Op2, Op3, Op4});


return false;


}
Outline of target-specific logics in each stage
38
Clang
Lowering
LLVM IR
Machine IR
AsmPrinter
• Simple validation on operand constraints

• Converting constraints
• Classifying constraints

• Constraint validations

• Lowering the operands
Print out di
ff
erent types of operands
39
Machine IR
INLINEASM &"move.l $0, %d1", ..., /* imm */ 87, /* clobber */ $d1
39
Machine IR
INLINEASM &"move.l $0, %d1", ..., /* imm */ 87, /* clobber */ $d1
39
Machine IR
INLINEASM &"move.l $0, %d1", ..., /* imm */ 87, /* clobber */ $d1
move.l #87, %d1
39
Machine IR
INLINEASM &"move.l $0, %d1", ..., /* imm */ 87, /* clobber */ $d1
move.l 87, %d1
move.l #87, %d1
Printing asm operands
40
bool AsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,


const char *, raw_ostream &O);


bool AsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,


const char *, raw_ostream &O);
Printing non-memory asm operands
M68k Example
41
void M68kAsmPrinter::printOperand(const MachineInstr *MI, int OpNum,


raw_ostream &OS) {


const MachineOperand &MO = MI->getOperand(OpNum);


switch (MO.getType()) {


case MachineOperand::MO_Register:


OS << "%" << M68kInstPrinter::getRegisterName(MO.getReg());


break;


case MachineOperand::MO_Immediate:


OS << '#' << MO.getImm();


break;


...


}


}
Printing non-memory asm operands
M68k Example
41
void M68kAsmPrinter::printOperand(const MachineInstr *MI, int OpNum,


raw_ostream &OS) {


const MachineOperand &MO = MI->getOperand(OpNum);


switch (MO.getType()) {


case MachineOperand::MO_Register:


OS << "%" << M68kInstPrinter::getRegisterName(MO.getReg());


break;


case MachineOperand::MO_Immediate:


OS << '#' << MO.getImm();


break;


...


}


}
Printing non-memory asm operands
M68k Example
41
void M68kAsmPrinter::printOperand(const MachineInstr *MI, int OpNum,


raw_ostream &OS) {


const MachineOperand &MO = MI->getOperand(OpNum);


switch (MO.getType()) {


case MachineOperand::MO_Register:


OS << "%" << M68kInstPrinter::getRegisterName(MO.getReg());


break;


case MachineOperand::MO_Immediate:


OS << '#' << MO.getImm();


break;


...


}


}
Epilogue
Advanced topics
Converting constraints in Clang

Asm dialects

Operand modifiers

Multi-alternative constraints

Constraint weights

Turn (simple) inline asm into LLVM code
43
Thank you!
44
GitHub: mshockwave

Email: minyihh@uci.edu 

25% book discount code: 25MINLLVM
Redeem:
Nov 15 ~ Nov 20, 2021
Appendix
Converting constraints in Clang
46
An operand constraint is assumed to have only a single character by default
Converting constraints in Clang
46
An operand constraint is assumed to have only a single character by default
Example: “Ci”
Converting constraints in Clang
46
An operand constraint is assumed to have only a single character by default
Example: “Ci” -> “^Ci”
Converting constraints in Clang
46
std::string


M68kTargetInfo::convertConstraint(const char *&Constraint) const override {


if (*Constraint == 'C')


// Two-character constraint; add "^" hint for later parsing


return std::string("^") + std::string(Constraint++, 2);


return std::string(1, *Constraint);


}
An operand constraint is assumed to have only a single character by default
Example: “Ci” -> “^Ci”
Operand constraint validations in Clang
Limitation on immediate value validations (cont’d)
47
bool M68kTargetInfo::validateAsmConstraint(const char *&Name, ConstraintInfo &info) const {


switch (*Name) {


…


case 'C':


++Name;


switch (*Name) {


case 'i': // constant integer


case 'j': // integer constant that doesn't fit in 16 bits


info.setRequiresImmediate();


return true;


}


break;


}


…


}

More Related Content

Handling inline assembly in Clang and LLVM

  • 1. Min-Yih “Min” Hsu @ LLVM Dev Meeting 2021 Handling inline assembly 
 in Clang and LLVM
  • 2. about:me “Min” Hsu • Computer Science PhD Candidate in University of California, Irvine • Code owner of M68k LLVM backend • Author of book “LLVM Techniques, Tips and Best Practices” (2021) 2
  • 3. How Inline Assembly is Processed in Clang & LLVM 3
  • 5. Introduction to inline assembly 5 void foo(...) { int sum = 0; bool flag = ...; if (flag) asm ("movl %%eax, %%ebxn" "addl %%ebx, %%esi" :::); else sum += 87; } * x86_64 assembly w/ AT&T syntax
  • 6. Introduction to inline assembly 5 void foo(...) { int sum = 0; bool flag = ...; if (flag) asm ("movl %%eax, %%ebxn" "addl %%ebx, %%esi" :::); else sum += 87; } * x86_64 assembly w/ AT&T syntax
  • 7. Introduction to inline assembly 5 void foo(...) { int sum = 0; bool flag = ...; if (flag) asm ("movl %%eax, %%ebxn" "addl %%ebx, %%esi" :::); else sum += 87; } foo: pushq %rbp movq %rsp, %rbp ... testb $1, -9(%rbp) je LBB0_2 ## InlineAsm Start movl %eax, %ebx addl %ebx, %esi ## InlineAsm End jmp LBB0_3 LBB0_2: movl -8(%rbp), %eax addl $87, %eax movl %eax, -8(%rbp) LBB0_3: popq %rbp retq * x86_64 assembly w/ AT&T syntax
  • 8. Introduction to inline assembly 5 void foo(...) { int sum = 0; bool flag = ...; if (flag) asm ("movl %%eax, %%ebxn" "addl %%ebx, %%esi" :::); else sum += 87; } foo: pushq %rbp movq %rsp, %rbp ... testb $1, -9(%rbp) je LBB0_2 ## InlineAsm Start movl %eax, %ebx addl %ebx, %esi ## InlineAsm End jmp LBB0_3 LBB0_2: movl -8(%rbp), %eax addl $87, %eax movl %eax, -8(%rbp) LBB0_3: popq %rbp retq * x86_64 assembly w/ AT&T syntax
  • 9. Introduction to inline assembly Why use inline assembly? 6 void foo(...) { int sum = 0; bool flag = ...; if (flag) asm ("movl %%eax, %%ebxn" "addl %%ebx, %%esi" :::); else sum += 87; } * x86_64 assembly w/ AT&T syntax
  • 10. Introduction to inline assembly Why use inline assembly? 6 void foo(...) { int sum = 0; bool flag = ...; if (flag) asm ("movl %%eax, %%ebxn" "addl %%ebx, %%esi" :::); else sum += 87; } Performance critical code * x86_64 assembly w/ AT&T syntax
  • 11. Introduction to inline assembly Why use inline assembly? 6 void foo(...) { int sum = 0; bool flag = ...; if (flag) asm ("movl %%eax, %%ebxn" "addl %%ebx, %%esi" :::); else sum += 87; } Performance critical code Low-level code e.g. Kernel, firmware * x86_64 assembly w/ AT&T syntax
  • 12. Introduction to inline assembly Why use inline assembly? 6 void foo(...) { int sum = 0; bool flag = ...; if (flag) asm ("movl %%eax, %%ebxn" "addl %%ebx, %%esi" :::); else sum += 87; } Performance critical code Low-level code e.g. Kernel, firmware Compiler optimizations “barrier” * x86_64 assembly w/ AT&T syntax
  • 13. Introduction to inline assembly 7 asm (“movl %%eax, %%ebxn" "addl %%ebx, %%esi" :::); * x86_64 assembly w/ AT&T syntax
  • 14. Introduction to inline assembly 7 asm (“movl %%eax, %%ebxn" "addl %%ebx, %%esi" :::); Assembly code (template) * x86_64 assembly w/ AT&T syntax
  • 15. Introduction to inline assembly 7 asm (“movl %%eax, %%ebxn" "addl %%ebx, %%esi" :::); Assembly code (template) * x86_64 assembly w/ AT&T syntax
  • 16. Introduction to inline assembly Output operands 8 int out_var; asm ("movl %%eax, %%ebxn" "addl %%ebx, %0" : "=r"(out_var) ::); * x86_64 assembly w/ AT&T syntax
  • 17. Introduction to inline assembly Output operands 8 int out_var; asm ("movl %%eax, %%ebxn" "addl %%ebx, %0" : "=r"(out_var) ::); * x86_64 assembly w/ AT&T syntax
  • 18. Introduction to inline assembly Output operands 8 int out_var; asm ("movl %%eax, %%ebxn" "addl %%ebx, %0" : "=r"(out_var) ::); Operand constraints * x86_64 assembly w/ AT&T syntax
  • 19. Introduction to inline assembly Operands constraints 9 int out_var; asm ("movl %%eax, %%ebxn" "addl %%ebx, %0" : "=r"(out_var) ::); * x86_64 assembly w/ AT&T syntax
  • 20. Introduction to inline assembly Operands constraints 9 int out_var; asm ("movl %%eax, %%ebxn" "addl %%ebx, %0" : "=r"(out_var) ::); * x86_64 assembly w/ AT&T syntax ## InlineAsm Start movl %eax, %ebx addl %ebx, %esi ## InlineAsm End
  • 21. Introduction to inline assembly Operands constraints 9 int out_var; asm ("movl %%eax, %%ebxn" "addl %%ebx, %0" : "=r"(out_var) ::); * x86_64 assembly w/ AT&T syntax ## InlineAsm Start movl %eax, %ebx addl %ebx, %esi ## InlineAsm End ## InlineAsm Start movl %eax, %ebx addl %ebx, -8(%ebp) ## InlineAsm End
  • 23. Operand constraints 10 • ‘r’ : General-purpose register operand • ‘i’ : Immediate integer operand • ‘m’ : Memory operand w/ arbitrary addressing mode Target-independent Constraints
  • 24. Operand constraints 10 • ‘r’ : General-purpose register operand • ‘i’ : Immediate integer operand • ‘m’ : Memory operand w/ arbitrary addressing mode Target-independent Constraints • ‘a’ : AL / AH / EAX / RAX • ‘I’ : Integer constant in the range of [0, 31] • ‘N’ : Unsigned 8-bit integer constant X86 Constraints
  • 25. Operand constraints 10 • ‘r’ : General-purpose register operand • ‘i’ : Immediate integer operand • ‘m’ : Memory operand w/ arbitrary addressing mode Target-independent Constraints • ‘a’ : AL / AH / EAX / RAX • ‘I’ : Integer constant in the range of [0, 31] • ‘N’ : Unsigned 8-bit integer constant X86 Constraints • ‘J’ : 16-bit signed integer constant • “Ci” : Constant integers • “Cj” : Constant signed integers that do NOT fi t in 16 bits M68k Constraints
  • 26. Operand constraints 10 • ‘r’ : General-purpose register operand • ‘i’ : Immediate integer operand • ‘m’ : Memory operand w/ arbitrary addressing mode Constraint Modifers Target-independent Constraints • ‘=‘ : This is an output operand • ‘+’ : This is an input / output operand • ‘a’ : AL / AH / EAX / RAX • ‘I’ : Integer constant in the range of [0, 31] • ‘N’ : Unsigned 8-bit integer constant X86 Constraints • ‘J’ : 16-bit signed integer constant • “Ci” : Constant integers • “Cj” : Constant signed integers that do NOT fi t in 16 bits M68k Constraints
  • 27. Introduction to inline assembly Input operands 11 int out_var, in_var; asm ("movl %1, %%ebxn" "addl %%ebx, %0" : “=r"(out_var) : “r”(in_var) :); * x86_64 assembly w/ AT&T syntax
  • 28. Introduction to inline assembly Input operands 11 int out_var, in_var; asm ("movl %1, %%ebxn" "addl %%ebx, %0" : “=r"(out_var) : “r”(in_var) :); * x86_64 assembly w/ AT&T syntax
  • 29. Introduction to inline assembly Input operands 11 int out_var, in_var; asm ("movl %1, %%ebxn" "addl %%ebx, %0" : “=r"(out_var) : “r”(in_var) :); Operand constraints * x86_64 assembly w/ AT&T syntax
  • 30. Introduction to inline assembly Clobber operands 12 int out_var, in_var; asm ("movl %1, %%ebxn" "addl %%ebx, %0" : “=r"(out_var) : “r”(in_var) : “ebx”); * x86_64 assembly w/ AT&T syntax
  • 31. Introduction to inline assembly Clobber operands 12 int out_var, in_var; asm ("movl %1, %%ebxn" "addl %%ebx, %0" : “=r"(out_var) : “r”(in_var) : “ebx”); * x86_64 assembly w/ AT&T syntax
  • 32. For more inline assembly syntax… 13 https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
  • 33. Handling inline assembly in Clang & LLVM Background 14
  • 34. Handling inline assembly in Clang & LLVM Background • Most parts of an inline assembly string are simply copied into the fi nal assembly fi le 14
  • 35. Handling inline assembly in Clang & LLVM Background • Most parts of an inline assembly string are simply copied into the fi nal assembly fi le • LLVM needs to “glue” inline assembly operands with the surrounding code 14
  • 36. Handling inline assembly in Clang & LLVM Background • Most parts of an inline assembly string are simply copied into the fi nal assembly fi le • LLVM needs to “glue” inline assembly operands with the surrounding code • Lots of target-speci fi c logics • In both Clang and the backend 14
  • 37. Handling inline assembly in Clang & LLVM Background • Most parts of an inline assembly string are simply copied into the fi nal assembly fi le • LLVM needs to “glue” inline assembly operands with the surrounding code • Lots of target-speci fi c logics • In both Clang and the backend • Target-speci fi c callbacks are scattered in the codebase • Documentation for this part is a little…shy 14
  • 39. Goals Learning inline assembly workflow in Clang / LLVM 15
  • 40. Goals Learning inline assembly workflow in Clang / LLVM A simple guide for backend developers to add inline assembly support 15
  • 41. Outline of target-specific logics in each stage 16 Clang Lowering LLVM IR Machine IR AsmPrinter
  • 42. Outline of target-specific logics in each stage 16 Clang Lowering LLVM IR Machine IR AsmPrinter • Simple validation on operand constraints • Converting constraints
  • 43. Outline of target-specific logics in each stage 16 Clang Lowering LLVM IR Machine IR AsmPrinter • Simple validation on operand constraints • Converting constraints • Classifying constraints • Constraint validations • Lowering the operands
  • 44. Outline of target-specific logics in each stage 16 Clang Lowering LLVM IR Machine IR AsmPrinter • Simple validation on operand constraints • Converting constraints • Classifying constraints • Constraint validations • Lowering the operands Print out di ff erent types of operands
  • 45. Outline of target-specific logics in each stage 17 Clang Lowering LLVM IR Machine IR AsmPrinter • Simple validation on operand constraints • Converting constraints • Classifying constraints • Constraint validations • Lowering the operands Print out di ff erent types of operands
  • 46. Operand constraint validations in Clang 18 bool TargetInfo::validateAsmConstraint(const char *&, ConstraintInfo &) const;
  • 47. Operand constraint validations in Clang 18 bool TargetInfo::validateAsmConstraint(const char *&, ConstraintInfo &) const; bool M68kTargetInfo::validateAsmConstraint(const char *&Name, ConstraintInfo &info) const { switch (*Name) { case 'a': // address register info.setAllowsRegister(); return true; } … }
  • 48. Operand constraint validations in Clang 19 bool TargetInfo::validateAsmConstraint(const char *&, ConstraintInfo &) const; bool M68kTargetInfo::validateAsmConstraint(const char *&Name, ConstraintInfo &info) const { switch (*Name) { case 'a': // address register info.setAllowsRegister(); return true; case 'J': // constant signed 16-bit integer info.setRequiresImmediate(std::numeric_limits<int16_t>::min(), std::numeric_limits<int16_t>::max()); return true; } … }
  • 49. Operand constraint validations in Clang Limitation on immediate value validations 20 void foo() { int32_t x; asm ("move.l %0, %%d1" : : "J" (x)); } * M68k assembly w/ Motorola syntax
  • 50. Operand constraint validations in Clang Limitation on immediate value validations 20 void foo() { int32_t x; asm ("move.l %0, %%d1" : : "J" (x)); } Constant signed 16-bit integer * M68k assembly w/ Motorola syntax
  • 51. Operand constraint validations in Clang Limitation on immediate value validations 20 void foo() { int32_t x; asm ("move.l %0, %%d1" : : "J" (x)); } $ clang -target m68k -fsyntax-only foo.c # No error $ clang -target m68k -emit-llvm foo.c # No error Constant signed 16-bit integer * M68k assembly w/ Motorola syntax
  • 52. Operand constraint validations in Clang Limitation on immediate value validations 20 void foo() { int32_t x; asm ("move.l %0, %%d1" : : "J" (x)); } $ clang -target m68k -fsyntax-only foo.c # No error $ clang -target m68k -emit-llvm foo.c # No error Constant signed 16-bit integer $ clang -target m68k -S foo.c error: constraint 'J' expects an integer constant expression * M68k assembly w/ Motorola syntax
  • 53. Inline assembly in LLVM IR 21 void foo() { const int x = 87; asm ("move.l %0, %%d1" : : "Ci" (x) : "d1"); } C/C++ * M68k assembly w/ Motorola syntax
  • 54. Inline assembly in LLVM IR 21 void foo() { const int x = 87; asm ("move.l %0, %%d1" : : "Ci" (x) : "d1"); } C/C++ call void asm sideeffect "move.l $0, %d1", “^Ci,~{d1}“(i32 87) LLVM IR * M68k assembly w/ Motorola syntax
  • 55. Inline assembly in LLVM IR 21 void foo() { const int x = 87; asm ("move.l %0, %%d1" : : "Ci" (x) : "d1"); } C/C++ call void asm sideeffect "move.l $0, %d1", “^Ci,~{d1}“(i32 87) LLVM IR * M68k assembly w/ Motorola syntax
  • 56. Inline assembly in LLVM IR 21 void foo() { const int x = 87; asm ("move.l %0, %%d1" : : "Ci" (x) : "d1"); } C/C++ call void asm sideeffect "move.l $0, %d1", “^Ci,~{d1}“(i32 87) LLVM IR * M68k assembly w/ Motorola syntax
  • 57. Inline assembly in LLVM IR 21 void foo() { const int x = 87; asm ("move.l %0, %%d1" : : "Ci" (x) : "d1"); } C/C++ call void asm sideeffect "move.l $0, %d1", “^Ci,~{d1}“(i32 87) LLVM IR * M68k assembly w/ Motorola syntax
  • 58. Inline assembly in LLVM IR 21 void foo() { const int x = 87; asm ("move.l %0, %%d1" : : "Ci" (x) : "d1"); } C/C++ call void asm sideeffect "move.l $0, %d1", “^Ci,~{d1}“(i32 87) LLVM IR * M68k assembly w/ Motorola syntax
  • 59. Outline of target-specific logics in each stage 22 Clang Lowering LLVM IR Machine IR AsmPrinter • Simple validation on operand constraints • Converting constraints • Classifying constraints • Constraint validations • Lowering the operands Print out di ff erent types of operands
  • 60. 23 LLVM IR call void asm sideeffect "move.l $0, %d1", “^Ci,~{d1}“(i32 87)
  • 61. 23 LLVM IR SelectionDAG call void asm sideeffect "move.l $0, %d1", “^Ci,~{d1}“(i32 87) t2: ch,glue = inlineasm ... "move.l $0, %d1", ..., TargetConstant:i32<87>, Register:i16 $d1
  • 62. 23 LLVM IR SelectionDAG Machine IR call void asm sideeffect "move.l $0, %d1", “^Ci,~{d1}“(i32 87) INLINEASM &"move.l $0, %d1", ..., /* imm */ 87, /* clobber */ $d1 t2: ch,glue = inlineasm ... "move.l $0, %d1", ..., TargetConstant:i32<87>, Register:i16 $d1
  • 63. 23 LLVM IR SelectionDAG Machine IR call void asm sideeffect "move.l $0, %d1", “^Ci,~{d1}“(i32 87) INLINEASM &"move.l $0, %d1", ..., /* imm */ 87, /* clobber */ $d1 t2: ch,glue = inlineasm ... "move.l $0, %d1", ..., TargetConstant:i32<87>, Register:i16 $d1
  • 68. Constraint classification 24 TargetLowering::ConstraintType M68kTargetLowering::getConstraintType(StringRef Constraint) const; • C_RegisterClass • C_Immediate • C_Memory Return: Ex: ’r’ Ex: ‘i’ Ex: ‘m’, ‘Q’ (AArch64)
  • 69. Constraint classification 24 TargetLowering::ConstraintType M68kTargetLowering::getConstraintType(StringRef Constraint) const; • C_RegisterClass • C_Immediate • C_Memory • C_Other Return: Ex: ’r’ Ex: ‘i’ Ex: ‘m’, ‘Q’ (AArch64)
  • 70. Lowering operands 25 getConstraintType Method in XXXTargetLowering Method in XXXISelDAGToDAG Will be invoked
  • 71. Lowering operands 25 getConstraintType getRegForInlineAsmConstraint C_RegisterClass Method in XXXTargetLowering Method in XXXISelDAGToDAG Will be invoked
  • 75. Lowering register operands 26 std::pair<unsigned, const TargetRegisterClass *> TargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI, StringRef Constraint, MVT VT) const;
  • 76. Lowering register operands 26 std::pair<unsigned, const TargetRegisterClass *> TargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI, StringRef Constraint, MVT VT) const; A speci fi c register or 0 if not applicable
  • 77. Lowering register operands 26 std::pair<unsigned, const TargetRegisterClass *> TargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI, StringRef Constraint, MVT VT) const; A speci fi c register or 0 if not applicable Valid register class to select from
  • 78. Lowering register operands M68k Example 27 std::pair<unsigned, const TargetRegisterClass *> M68kTargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *, StringRef Constraint, MVT VT) const { switch (Constraint[0]) { case 'a': switch (VT.SimpleTy) { case MVT::i16: return std::make_pair(0U, &M68k::AR16RegClass); } } }
  • 79. Lowering register operands M68k Example 27 std::pair<unsigned, const TargetRegisterClass *> M68kTargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *, StringRef Constraint, MVT VT) const { switch (Constraint[0]) { case 'a': switch (VT.SimpleTy) { case MVT::i16: return std::make_pair(0U, &M68k::AR16RegClass); } } }
  • 80. Lowering register operands X86 Example 28 std::pair<unsigned, const TargetRegisterClass *> X86TargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *, StringRef Constraint, MVT VT) const { ... if (Constraint == "Yz") { // First SSE register (%xmm0). switch (VT.SimpleTy) { case MVT::f32: case MVT::i32: return std::make_pair(X86::XMM0, &X86::FR32RegClass); } } }
  • 81. Lowering register operands X86 Example 28 std::pair<unsigned, const TargetRegisterClass *> X86TargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *, StringRef Constraint, MVT VT) const { ... if (Constraint == "Yz") { // First SSE register (%xmm0). switch (VT.SimpleTy) { case MVT::f32: case MVT::i32: return std::make_pair(X86::XMM0, &X86::FR32RegClass); } } }
  • 82. Lowering immediate / other operands 29 void TargetLowering:: LowerAsmOperandForConstraint(SDValue Op, std::string &Constraint, std::vector<SDValue> &Ops, SelectionDAG &DAG) const;
  • 83. Lowering immediate / other operands M68k Example 30 void M68kTargetLowering::LowerAsmOperandForConstraint(SDValue Op, std::string &Constraint, std::vector<SDValue> &Ops, SelectionDAG &DAG) const { switch (Constraint[0]) { case 'J': { // constant signed 16-bit integer } } }
  • 84. Lowering immediate / other operands M68k Example 31 void M68kTargetLowering::LowerAsmOperandForConstraint(SDValue Op, std::string &Constraint, std::vector<SDValue> &Ops, SelectionDAG &DAG) const { switch (Constraint[0]) { case 'J': { // constant signed 16-bit integer if (auto *C = dyn_cast<ConstantSDNode>(Op)) { int64_t Val = C->getSExtValue(); } } } }
  • 85. Lowering immediate / other operands M68k Example 32 void M68kTargetLowering::LowerAsmOperandForConstraint(SDValue Op, std::string &Constraint, std::vector<SDValue> &Ops, SelectionDAG &DAG) const { switch (Constraint[0]) { case 'J': { // constant signed 16-bit integer if (auto *C = dyn_cast<ConstantSDNode>(Op)) { int64_t Val = C->getSExtValue(); if (isInt<16>(Val)) { Ops.push_back(Op); return; } } } } }
  • 86. Lowering memory operands Memory constraint classi fi cation 33 unsigned TargetLowering::getInlineAsmMemConstraint(StringRef ConstraintCode) const;
  • 87. Lowering memory operands Memory constraint classi fi cation 33 unsigned TargetLowering::getInlineAsmMemConstraint(StringRef ConstraintCode) const; Return:
  • 88. Lowering memory operands Memory constraint classi fi cation 33 unsigned TargetLowering::getInlineAsmMemConstraint(StringRef ConstraintCode) const; • InlineAsm::Constraint_m • InlineAsm::Constraint_o • InlineAsm::Constraint_v Return: Generic memory constraints
  • 89. Lowering memory operands Memory constraint classi fi cation 33 unsigned TargetLowering::getInlineAsmMemConstraint(StringRef ConstraintCode) const; • InlineAsm::Constraint_m • InlineAsm::Constraint_o • InlineAsm::Constraint_v Return: • InlineAsm::Constraint_A • InlineAsm::Constraint_Q Generic memory constraints Target-speci fi c constraints!
  • 90. Lowering memory operands Memory constraint classi fi cation — RISCV Example 34 unsigned RISCVTargetLowering:: getInlineAsmMemConstraint(StringRef ConstraintCode) const { switch (ConstraintCode[0]) { case 'A': return InlineAsm::Constraint_A; ... } ... }
  • 91. Lowering memory operands 35 bool SelectionDAGISel::SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps);
  • 92. Lowering memory operands RISCV Example 36 bool RISCVDAGToDAGISel::SelectInlineAsmMemoryOperand( const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) { switch (ConstraintID) { case InlineAsm::Constraint_A: OutOps.push_back(Op); return false; default: break; } return true; }
  • 93. Lowering memory operands Complex addressing mode — X86 Example 37 bool X86DAGToDAGISel:: SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) { SDValue Op0, Op1, Op2, Op3, Op4; switch (ConstraintID) { case InlineAsm::Constraint_m: // memory if (!selectAddr(nullptr, Op, Op0, Op1, Op2, Op3, Op4)) return true; break; } OutOps.insert(OutOps.end(), {Op0, Op1, Op2, Op3, Op4}); return false; }
  • 94. Lowering memory operands Complex addressing mode — X86 Example 37 bool X86DAGToDAGISel:: SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) { SDValue Op0, Op1, Op2, Op3, Op4; switch (ConstraintID) { case InlineAsm::Constraint_m: // memory if (!selectAddr(nullptr, Op, Op0, Op1, Op2, Op3, Op4)) return true; break; } OutOps.insert(OutOps.end(), {Op0, Op1, Op2, Op3, Op4}); return false; }
  • 95. Outline of target-specific logics in each stage 38 Clang Lowering LLVM IR Machine IR AsmPrinter • Simple validation on operand constraints • Converting constraints • Classifying constraints • Constraint validations • Lowering the operands Print out di ff erent types of operands
  • 96. 39 Machine IR INLINEASM &"move.l $0, %d1", ..., /* imm */ 87, /* clobber */ $d1
  • 97. 39 Machine IR INLINEASM &"move.l $0, %d1", ..., /* imm */ 87, /* clobber */ $d1
  • 98. 39 Machine IR INLINEASM &"move.l $0, %d1", ..., /* imm */ 87, /* clobber */ $d1 move.l #87, %d1
  • 99. 39 Machine IR INLINEASM &"move.l $0, %d1", ..., /* imm */ 87, /* clobber */ $d1 move.l 87, %d1 move.l #87, %d1
  • 100. Printing asm operands 40 bool AsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, const char *, raw_ostream &O); bool AsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo, const char *, raw_ostream &O);
  • 101. Printing non-memory asm operands M68k Example 41 void M68kAsmPrinter::printOperand(const MachineInstr *MI, int OpNum, raw_ostream &OS) { const MachineOperand &MO = MI->getOperand(OpNum); switch (MO.getType()) { case MachineOperand::MO_Register: OS << "%" << M68kInstPrinter::getRegisterName(MO.getReg()); break; case MachineOperand::MO_Immediate: OS << '#' << MO.getImm(); break; ... } }
  • 102. Printing non-memory asm operands M68k Example 41 void M68kAsmPrinter::printOperand(const MachineInstr *MI, int OpNum, raw_ostream &OS) { const MachineOperand &MO = MI->getOperand(OpNum); switch (MO.getType()) { case MachineOperand::MO_Register: OS << "%" << M68kInstPrinter::getRegisterName(MO.getReg()); break; case MachineOperand::MO_Immediate: OS << '#' << MO.getImm(); break; ... } }
  • 103. Printing non-memory asm operands M68k Example 41 void M68kAsmPrinter::printOperand(const MachineInstr *MI, int OpNum, raw_ostream &OS) { const MachineOperand &MO = MI->getOperand(OpNum); switch (MO.getType()) { case MachineOperand::MO_Register: OS << "%" << M68kInstPrinter::getRegisterName(MO.getReg()); break; case MachineOperand::MO_Immediate: OS << '#' << MO.getImm(); break; ... } }
  • 105. Advanced topics Converting constraints in Clang Asm dialects Operand modifiers Multi-alternative constraints Constraint weights Turn (simple) inline asm into LLVM code 43
  • 106. Thank you! 44 GitHub: mshockwave Email: minyihh@uci.edu 25% book discount code: 25MINLLVM Redeem: Nov 15 ~ Nov 20, 2021
  • 108. Converting constraints in Clang 46 An operand constraint is assumed to have only a single character by default
  • 109. Converting constraints in Clang 46 An operand constraint is assumed to have only a single character by default Example: “Ci”
  • 110. Converting constraints in Clang 46 An operand constraint is assumed to have only a single character by default Example: “Ci” -> “^Ci”
  • 111. Converting constraints in Clang 46 std::string M68kTargetInfo::convertConstraint(const char *&Constraint) const override { if (*Constraint == 'C') // Two-character constraint; add "^" hint for later parsing return std::string("^") + std::string(Constraint++, 2); return std::string(1, *Constraint); } An operand constraint is assumed to have only a single character by default Example: “Ci” -> “^Ci”
  • 112. Operand constraint validations in Clang Limitation on immediate value validations (cont’d) 47 bool M68kTargetInfo::validateAsmConstraint(const char *&Name, ConstraintInfo &info) const { switch (*Name) { … case 'C': ++Name; switch (*Name) { case 'i': // constant integer case 'j': // integer constant that doesn't fit in 16 bits info.setRequiresImmediate(); return true; } break; } … }