Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SOL] Implement syscall instruction #121

Merged
merged 3 commits into from
Dec 19, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion llvm/lib/Target/SBF/Disassembler/SBFDisassembler.cpp
Original file line number Diff line number Diff line change
@@ -69,6 +69,7 @@ class SBFDisassembler : public MCDisassembler {
uint8_t getInstMode(uint64_t Inst) const { return (Inst >> 61) & 0x7; };
bool isMov32(uint64_t Inst) const { return (Inst >> 56) == 0xb4; }
bool isNewMem(uint64_t Inst) const;
bool isSyscallOrExit(uint64_t Inst) const { return (Inst >> 56) == 0x95; }
};

} // end anonymous namespace
@@ -202,7 +203,8 @@ DecodeStatus SBFDisassembler::getInstruction(MCInst &Instr, uint64_t &Size,
Result =
decodeInstruction(DecoderTableSBFALU32MEMv264,
Instr, Insn, Address, this, STI);
else if (isNewMem(Insn) && STI.hasFeature(SBF::FeatureNewMemEncoding))
else if ((isNewMem(Insn) && STI.hasFeature(SBF::FeatureNewMemEncoding)) ||
(isSyscallOrExit(Insn) && STI.hasFeature(SBF::FeatureStaticSyscalls)))
Result =
decodeInstruction(DecoderTableSBFv264,
Instr, Insn, Address, this, STI);
11 changes: 9 additions & 2 deletions llvm/lib/Target/SBF/SBFISelLowering.cpp
Original file line number Diff line number Diff line change
@@ -592,11 +592,16 @@ SDValue SBFTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
// If the callee is a GlobalAddress node (quite common, every direct call is)
// turn it into a TargetGlobalAddress node so that legalize doesn't hack it.
// Likewise ExternalSymbol -> TargetExternalSymbol.
unsigned NodeCode = SBFISD::CALL;
if (GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(Callee)) {
Callee = DAG.getTargetGlobalAddress(G->getGlobal(), CLI.DL, PtrVT,
G->getOffset(), 0);
} else if (ExternalSymbolSDNode *E = dyn_cast<ExternalSymbolSDNode>(Callee)) {
Callee = DAG.getTargetExternalSymbol(E->getSymbol(), PtrVT, 0);
} else if (isa<ConstantSDNode>(Callee) && Subtarget->getHasStaticSyscalls()) {
// When static syscalls are enabled and we have a constant operand for call,
// we emit a syscall.
NodeCode = SBFISD::SYSCALL;
}

// Returns a chain & a flag for retval copy to use.
@@ -610,14 +615,14 @@ SDValue SBFTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
for (auto &Reg : RegsToPass)
Ops.push_back(DAG.getRegister(Reg.first, Reg.second.getValueType()));

if (HasStackArgs) {
if (HasStackArgs && !Subtarget->getHasDynamicFrames()) {
Ops.push_back(DAG.getRegister(SBF::R5, MVT::i64));
}

if (InGlue.getNode())
Ops.push_back(InGlue);

Chain = DAG.getNode(SBFISD::CALL, CLI.DL, NodeTys, Ops);
Chain = DAG.getNode(NodeCode, CLI.DL, NodeTys, Ops);
InGlue = Chain.getValue(1);

DAG.addNoMergeSiteInfo(Chain.getNode(), CLI.NoMerge);
@@ -919,6 +924,8 @@ const char *SBFTargetLowering::getTargetNodeName(unsigned Opcode) const {
return "SBFISD::Wrapper";
case SBFISD::MEMCPY:
return "SBFISD::MEMCPY";
case SBFISD::SYSCALL:
return "SBFISD::SYSCALL";
}
return nullptr;
}
3 changes: 2 additions & 1 deletion llvm/lib/Target/SBF/SBFISelLowering.h
Original file line number Diff line number Diff line change
@@ -28,7 +28,8 @@ enum NodeType : unsigned {
SELECT_CC,
BR_CC,
Wrapper,
MEMCPY
MEMCPY,
SYSCALL,
};
}

27 changes: 14 additions & 13 deletions llvm/lib/Target/SBF/SBFInstrFormats.td
Original file line number Diff line number Diff line change
@@ -72,19 +72,20 @@ class SBFJumpOp<bits<4> val> {
bits<4> Value = val;
}

def SBF_JA : SBFJumpOp<0x0>;
def SBF_JEQ : SBFJumpOp<0x1>;
def SBF_JGT : SBFJumpOp<0x2>;
def SBF_JGE : SBFJumpOp<0x3>;
def SBF_JNE : SBFJumpOp<0x5>;
def SBF_JSGT : SBFJumpOp<0x6>;
def SBF_JSGE : SBFJumpOp<0x7>;
def SBF_CALL : SBFJumpOp<0x8>;
def SBF_EXIT : SBFJumpOp<0x9>;
def SBF_JLT : SBFJumpOp<0xa>;
def SBF_JLE : SBFJumpOp<0xb>;
def SBF_JSLT : SBFJumpOp<0xc>;
def SBF_JSLE : SBFJumpOp<0xd>;
def SBF_JA : SBFJumpOp<0x0>;
def SBF_JEQ : SBFJumpOp<0x1>;
def SBF_JGT : SBFJumpOp<0x2>;
def SBF_JGE : SBFJumpOp<0x3>;
def SBF_JNE : SBFJumpOp<0x5>;
def SBF_JSGT : SBFJumpOp<0x6>;
def SBF_JSGE : SBFJumpOp<0x7>;
def SBF_CALL : SBFJumpOp<0x8>;
def SBF_EXIT : SBFJumpOp<0x9>;
def SBF_JLT : SBFJumpOp<0xa>;
def SBF_JLE : SBFJumpOp<0xb>;
def SBF_JSLT : SBFJumpOp<0xc>;
def SBF_JSLE : SBFJumpOp<0xd>;
def SBF_SYSCALL : SBFJumpOp<0x9>;

class SBFWidthModifer<bits<2> val> {
bits<2> Value = val;
22 changes: 20 additions & 2 deletions llvm/lib/Target/SBF/SBFInstrInfo.td
Original file line number Diff line number Diff line change
@@ -32,10 +32,13 @@ def SDT_SBFMEMCPY : SDTypeProfile<0, 4, [SDTCisVT<0, i64>,
SDTCisVT<1, i64>,
SDTCisVT<2, i64>,
SDTCisVT<3, i64>]>;
def SDT_SBFSyscall : SDTypeProfile<0, 1, [SDTCisVT<0, i64>]>;

def SBFcall : SDNode<"SBFISD::CALL", SDT_SBFCall,
[SDNPHasChain, SDNPOptInGlue, SDNPOutGlue,
SDNPVariadic]>;
def SBFSyscall : SDNode<"SBFISD::SYSCALL", SDT_SBFSyscall,
[SDNPHasChain, SDNPOptInGlue, SDNPOutGlue]>;
def SBFretglue : SDNode<"SBFISD::RET_GLUE", SDTNone,
[SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>;
def SBFcallseq_start: SDNode<"ISD::CALLSEQ_START", SDT_SBFCallSeqStart,
@@ -801,6 +804,19 @@ class CALL<string OpcodeStr>
let SBFClass = SBF_JMP;
}

class SYSCALL<string OpcodeStr>
: TYPE_ALU_JMP<SBF_SYSCALL.Value, SBF_K.Value,
(outs),
(ins i64imm:$imm),
!strconcat(OpcodeStr, " $imm"),
[]> {
bits<32> imm;

let Inst{31-0} = imm;
let SBFClass = SBF_JMP;
}


class CALLX<string OpcodeStr>
: TYPE_ALU_JMP<SBF_CALL.Value, SBF_X.Value,
(outs),
@@ -838,6 +854,7 @@ let isCall=1, hasDelaySlot=0, Uses = [R10],
def JALX : CALLX<"callx">, Requires<[SBFNoCallxSrc]>;
let DecoderNamespace = "SBFv2" in {
def JALX_v2 : CALLX_SRC_REG<"callx">, Requires<[SBFCallxSrc]>;
def SYSCALL_v3 : SYSCALL<"syscall">, Requires<[SBFHasStaticSyscalls]>;
}
}

@@ -883,7 +900,7 @@ class RETURN<string OpcodeStr>

let isReturn = 1, isTerminator = 1, hasDelaySlot=0, isBarrier = 1,
isNotDuplicable = 1, Predicates = [SBFHasStaticSyscalls] in {
def RETURN : RETURN<"return">;
def RETURN_v3 : RETURN<"return">;
}

// ADJCALLSTACKDOWN/UP pseudo insns
@@ -946,9 +963,10 @@ def : Pat<(i64 (and (i64 GPR:$src), 0xffffFFFF)),
// Calls
def : Pat<(SBFcall tglobaladdr:$dst), (JAL tglobaladdr:$dst)>;
def : Pat<(SBFcall texternalsym:$dst), (JAL texternalsym:$dst)>;
def : Pat<(SBFcall imm:$dst), (JAL imm:$dst)>;
def : Pat<(SBFcall imm:$dst), (JAL imm:$dst)>, Requires<[SBFNoStaticSyscalls]>;
def : Pat<(SBFcall GPR:$dst), (JALX GPR:$dst)>, Requires<[SBFNoCallxSrc]>;
def : Pat<(SBFcall GPR:$dst), (JALX_v2 GPR:$dst)>, Requires<[SBFCallxSrc]>;
def : Pat<(SBFSyscall imm:$imm), (SYSCALL_v3 imm:$imm)>, Requires<[SBFHasStaticSyscalls]>;

// Loads
let Predicates = [SBFNoALU32, SBFOldMemEncoding] in {
15 changes: 9 additions & 6 deletions llvm/test/CodeGen/SBF/call_internal.ll
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
; RUN: llc < %s -march=sbf --show-mc-encoding | FileCheck --check-prefix=CHECK-ASM %s
; RUN: llc -march=sbf --filetype=obj -o - %s | llvm-objdump -d - | FileCheck --check-prefix=CHECK-OBJ %s
; RUN: llc < %s -march=sbf -mcpu=sbfv2 --show-mc-encoding | FileCheck --check-prefix=CHECK-ASM %s
; RUN: llc < %s -march=sbf --show-mc-encoding | FileCheck --check-prefixes=CHECK-ASM,CHECK-ASM-V0 %s
; RUN: llc -march=sbf --filetype=obj -o - %s | llvm-objdump -d - | FileCheck --check-prefixes=CHECK-OBJ,CHECK-OBJ-V0 %s
; RUN: llc < %s -march=sbf -mcpu=sbfv2 --show-mc-encoding | FileCheck --check-prefixes=CHECK-ASM,CHECK-ASM-V3 %s
; RUN: llc -march=sbf -mcpu=sbfv2 --filetype=obj -o - %s | llvm-objdump -d -
; | FileCheck --check-prefix=CHECK-OBJ %s
; | FileCheck --check-prefixes=CHECK-OBJ,CHECK-OBJ-V3 %s

@.str = private unnamed_addr constant [5 x i8] c"foo\0A\00", align 1

@@ -16,8 +16,11 @@ entry:
; Function Attrs: nounwind
define dso_local i64 @entrypoint(ptr noundef %input) local_unnamed_addr #1 {
entry:
; CHECK-ASM: call 1811268606 # encoding: [0x85,0x00,0x00,0x00,0xfe,0xc3,0xf5,0x6b]
; CHECK-OBJ: 85 00 00 00 fe c3 f5 6b call 0x6bf5c3fe
; CHECK-ASM-V0: call 1811268606 # encoding: [0x85,0x00,0x00,0x00,0xfe,0xc3,0xf5,0x6b]
; CHECK-ASM-V3: syscall 1811268606 # encoding: [0x95,0x00,0x00,0x00,0xfe,0xc3,0xf5,0x6b]

; CHECK-OBJ-V0: 85 00 00 00 fe c3 f5 6b call 0x6bf5c3fe
; CHECK-OBJ-V3: 95 00 00 00 fe c3 f5 6b syscall 0x6bf5c3fe

tail call void inttoptr (i64 1811268606 to ptr)(ptr noundef nonnull @.str, i64 noundef 4) #3
%add.ptr = getelementptr inbounds i8, ptr %input, i64 4
25 changes: 25 additions & 0 deletions llvm/test/CodeGen/SBF/static_syscall.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
; RUN: llc -march=sbf < %s | FileCheck --check-prefix=CHECK-V0 %s
; RUN: llc -march=sbf -mattr=+static-syscalls -show-mc-encoding < %s | FileCheck --check-prefix=CHECK-V3 %s


; Function Attrs: nounwind
define dso_local i32 @test(i32 noundef %a, i32 noundef %b) {
entry:
; CHECK-LABEL: test

; CHECK-V0: call 2
; CHECK-V3 syscall 2 # encoding: [0x95,0x00,0x00,0x00,0x01,0x00,0x00,0x00]
%syscall_1 = tail call i32 inttoptr (i64 2 to ptr)(i32 noundef %a, i32 noundef %b)

; CHECK-V0: call 11
; CHECK-V3: syscall 11 # encoding: [0x95,0x00,0x00,0x00,0x0b,0x00,0x00,0x00]
%syscall_2 = tail call i32 inttoptr (i64 11 to ptr)(i32 noundef %a, i32 noundef %b)

; CHECK-V0: call 112
; CHECK-V3: syscall 112
%syscall_3 = tail call i32 inttoptr (i64 112 to ptr)(i32 noundef %a, i32 noundef %b)

%add_1 = add i32 %syscall_1, %syscall_2
%add_2 = add i32 %add_1, %syscall_3
ret i32 %add_1
}
50 changes: 50 additions & 0 deletions llvm/test/CodeGen/SBF/static_syscall_2.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
; RUN: llc -march=sbf -mattr=+static-syscalls < %s | FileCheck --check-prefix=CHECK %s

; Syscall declaration in C:
;
; int c_declaration(int a, int b) {
; int (*const syscall)(int a, int b) = (void*)50;
; return syscall(a, b);
; }
; The following is the unoptimized output from clang:

define dso_local i32 @c_declaration(i32 noundef %a, i32 noundef %b) #0 {
entry:
%a.addr = alloca i32, align 4
%b.addr = alloca i32, align 4
%syscall = alloca ptr, align 8
store i32 %a, ptr %a.addr, align 4
store i32 %b, ptr %b.addr, align 4
store ptr inttoptr (i64 50 to ptr), ptr %syscall, align 8
%0 = load i32, ptr %a.addr, align 4
%1 = load i32, ptr %b.addr, align 4

; Ensure the syscall instruction is emitted
; CHECK: syscall 50

%call = call i32 inttoptr (i64 50 to ptr)(i32 noundef %0, i32 noundef %1)
ret i32 %call
}

; Syscall declaration in Rust:
;
; #[no_mangle]
; pub unsafe fn rust_declaration(b: u64) -> u32 {
; let syscall : extern "C" fn(b: u64) -> u32 = core::mem::transmute(60u64);
; return syscall(b);
; }
; The following is the unoptimized output from rustc:
Comment on lines +29 to +36
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In some circumstances, specially when we build the code with -O0, the front end may emit a callx. Instead of creating a peephole or forcing optimizations in the backend, I decided to add the correct declaration example in both C and Rust.

These examples generate LLVM-IR code that even when unoptimized will create a syscall instruction.


define i32 @rust_declaration(i64 %b) unnamed_addr {
start:
%syscall.dbg.spill = alloca [8 x i8], align 8
%b.dbg.spill = alloca [8 x i8], align 8
store i64 %b, ptr %b.dbg.spill, align 8
store ptr getelementptr (i8, ptr null, i64 60), ptr %syscall.dbg.spill, align 8

; Ensure the syscall instruction is emitted
; CHECK: syscall 60

%_0 = call i32 getelementptr (i8, ptr null, i64 60)(i64 %b)
ret i32 %_0
}
6 changes: 6 additions & 0 deletions llvm/test/MC/Disassembler/SBF/sbf-jmp.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# RUN: llvm-mc --disassemble %s -triple=sbf-solana-solana \
# RUN: | FileCheck %s --check-prefix=CHECK-NEW
# RUN: llvm-mc --disassemble %s -triple=sbf-solana-solana -mattr=+static-syscalls \
# RUN: | FileCheck %s --check-prefix=CHECK-V2

# TODO: Test immediate field ranges.

@@ -109,5 +111,9 @@
# CHECK-NEW: exit
0x95,0x00,0x00,0x00,0x00,0x00,0x00,0x00

# CHECK-V2: syscall 5
0x95,0x00,0x00,0x00,0x05,0x00,0x00,0x00


# CHECK-NEW: return
0x9d,0x00,0x00,0x00,0x00,0x00,0x00,0x00
4 changes: 1 addition & 3 deletions llvm/test/MC/SBF/insn-unit.s
Original file line number Diff line number Diff line change
@@ -45,10 +45,8 @@
// ======== BPF_JMP Class ========
ja Llabel0 // BPF_JA
call 1 // BPF_CALL
exit // BPF_EXIT
// CHECK: 05 00 1a 00 00 00 00 00 ja +0x1a
// CHECK: 05 00 19 00 00 00 00 00 ja +0x19
// CHECK: 85 00 00 00 01 00 00 00 call 0x1
// CHECK: 95 00 00 00 00 00 00 00 exit

jeq r0, r1, Llabel0 // BPF_JEQ | BPF_X
jne r3, r4, Llabel0 // BPF_JNE | BPF_X
4 changes: 2 additions & 2 deletions llvm/test/MC/SBF/sbf-jmp.s
Original file line number Diff line number Diff line change
@@ -163,6 +163,6 @@ call 8
# CHECK-ASM-OLD: encoding: [0x8d,0x00,0x00,0x00,0x04,0x00,0x00,0x00]
callx r4

# CHECK-OBJ-NEW: exit
# CHECK-ASM-NEW: encoding: [0x95,0x00,0x00,0x00,0x00,0x00,0x00,0x00]
# CHECK-OBJ-OLD: exit
# CHECK-ASM-OLD: encoding: [0x95,0x00,0x00,0x00,0x00,0x00,0x00,0x00]
exit
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# RUN: llvm-mc -triple=sbf-solana-solana --mcpu=sbfv2 -filetype=obj -o %t %s
# RUN: llvm-objdump -d -r %t | FileCheck --check-prefix=CHECK %s

syscall 9
return

// CHECK: 95 00 00 00 09 00 00 00 syscall 0x9
// CHECK: 9d 00 00 00 00 00 00 00 return