Skip to content

Commit e35a48a

Browse files
committed
iop: compile using LLVM
1 parent 7336c61 commit e35a48a

File tree

4 files changed

+305
-9
lines changed

4 files changed

+305
-9
lines changed

Defs.make

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,22 @@ EE_PKG_CONFIG ?= $(EE_TOOL_PREFIX)pkg-config
3030
# Defintions for the IOP toolchain.
3131
#
3232

33+
# Set IOP_USE_LLVM=1 to use LLVM/Clang instead of GCC
34+
IOP_USE_LLVM ?= 0
35+
36+
ifeq ($(IOP_USE_LLVM),1)
37+
# LLVM/Clang toolchain for IOP
38+
IOP_LLVM_PREFIX ?= $(PS2DEV)/llvm/bin/
39+
IOP_CC ?= $(IOP_LLVM_PREFIX)clang
40+
IOP_AS ?= $(IOP_LLVM_PREFIX)clang
41+
IOP_LD ?= $(IOP_LLVM_PREFIX)ld.lld
42+
IOP_AR ?= $(IOP_LLVM_PREFIX)llvm-ar
43+
IOP_OBJCOPY ?= $(IOP_LLVM_PREFIX)llvm-objcopy
44+
IOP_STRIP ?= $(IOP_LLVM_PREFIX)llvm-strip
45+
IOP_ADDR2LINE ?= $(IOP_LLVM_PREFIX)llvm-addr2line
46+
IOP_RANLIB ?= $(IOP_LLVM_PREFIX)llvm-ranlib
47+
else
48+
# GCC toolchain for IOP (default)
3349
IOP_TOOL_PREFIX ?= mipsel-none-elf-
3450
IOP_CC ?= $(IOP_TOOL_PREFIX)gcc
3551
IOP_AS ?= $(IOP_TOOL_PREFIX)as
@@ -39,6 +55,7 @@ IOP_OBJCOPY ?= $(IOP_TOOL_PREFIX)objcopy
3955
IOP_STRIP ?= $(IOP_TOOL_PREFIX)strip
4056
IOP_ADDR2LINE ?= $(IOP_TOOL_PREFIX)addr2line
4157
IOP_RANLIB ?= $(IOP_TOOL_PREFIX)ranlib
58+
endif
4259

4360
#
4461
# Definitions for the local toolchain

iop/Rules.make

Lines changed: 89 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,13 @@
66
# Licenced under Academic Free License version 2.0
77
# Review ps2sdk README & LICENSE files for further details.
88

9-
IOP_CC_VERSION := $(shell $(IOP_CC) -dumpversion)
9+
# Comma definition for use in $(addprefix)
10+
comma := ,
1011

1112
IOP_OBJS_DIR ?= obj/
1213
IOP_SRC_DIR ?= src/
1314
IOP_INC_DIR ?= include/
1415

15-
ifeq ($(IOP_CC_VERSION),3.2.2)
16-
ASFLAGS_TARGET = -march=r3000
17-
endif
18-
19-
ifeq ($(IOP_CC_VERSION),3.2.3)
20-
ASFLAGS_TARGET = -march=r3000
21-
endif
22-
2316
# include dir
2417
IOP_INCS := $(IOP_INCS) -I$(IOP_SRC_DIR) -I$(IOP_SRC_DIR)include -I$(IOP_INC_DIR) -I$(PS2SDKSRC)/iop/kernel/include -I$(PS2SDKSRC)/common/include
2518

@@ -33,6 +26,63 @@ IOP_OPTFLAGS ?= -Os
3326
# Warning compiler flags
3427
IOP_WARNFLAGS ?= -Wall -Werror
3528

29+
ifeq ($(IOP_USE_LLVM),1)
30+
# =====================
31+
# LLVM/Clang settings
32+
# =====================
33+
34+
# Target flags for IOP (MIPS R3000 - 32-bit little endian)
35+
# -mabi=32: Use O32 ABI (standard 32-bit MIPS ABI)
36+
# -mno-abicalls: Match GCC's default non-PIC code generation
37+
IOP_TARGET_FLAGS := --target=mipsel-none-elf -march=mips1 -mcpu=mips1 -mabi=32 -mno-abicalls
38+
39+
# Debug information flags (LLVM compatible)
40+
IOP_DBGINFOFLAGS ?= -gdwarf-4
41+
42+
# C compiler flags for LLVM/Clang
43+
# -fno-builtin prevents built-in functions from being included
44+
# -msoft-float ensures software floating point (IOP has no FPU)
45+
# -G0 disables small data section optimization
46+
# -mno-explicit-relocs ensures paired HI16/LO16 relocations (required for IRX1 format)
47+
# -mllvm --mno-check-zero-division disables trap instructions for division by zero
48+
# (required because MIPS1 doesn't have trap instructions)
49+
# -Qunused-arguments silences warnings about unused arguments when used with linker
50+
# -U__mips -D__mips=1 fixes LLVM incorrectly setting __mips=32 for MIPS-I
51+
IOP_CFLAGS := $(IOP_TARGET_FLAGS) -D_IOP -U__mips -D__mips=1 -ffreestanding -fno-builtin -fno-builtin-memset -fno-builtin-memcpy -fno-builtin-memmove -msoft-float -mno-explicit-relocs -G0 -fomit-frame-pointer -mllvm --mno-check-zero-division -Qunused-arguments $(IOP_OPTFLAGS) $(IOP_WARNFLAGS) $(IOP_DBGINFOFLAGS) $(IOP_INCS) $(IOP_CFLAGS)
52+
ifeq ($(DEBUG),1)
53+
IOP_CFLAGS += -DDEBUG
54+
endif
55+
56+
# Import/export table flags for LLVM
57+
IOP_IETABLE_CFLAGS :=
58+
59+
# Linker flags for LLVM/Clang with LLD
60+
# -nostdlib: don't link standard libraries
61+
# -Wl,-r: create relocatable output
62+
# -Wl,--no-gc-sections: keep all sections
63+
IOP_LDFLAGS := -nostdlib -fuse-ld=lld -Wl,-r -Wl,--no-gc-sections $(IOP_LDFLAGS)
64+
65+
# Assembler flags for LLVM (using clang as assembler)
66+
IOP_ASFLAGS := $(IOP_TARGET_FLAGS) -msoft-float $(IOP_ASFLAGS)
67+
68+
# Default link file for LLVM (LLD-compatible)
69+
IOP_LINKFILE_DEFAULT := $(PS2SDKSRC)/iop/startup/src/linkfile.lld
70+
71+
else
72+
# =====================
73+
# GCC settings (default)
74+
# =====================
75+
76+
IOP_CC_VERSION := $(shell $(IOP_CC) -dumpversion)
77+
78+
ifeq ($(IOP_CC_VERSION),3.2.2)
79+
ASFLAGS_TARGET = -march=r3000
80+
endif
81+
82+
ifeq ($(IOP_CC_VERSION),3.2.3)
83+
ASFLAGS_TARGET = -march=r3000
84+
endif
85+
3686
# Debug information flags
3787
IOP_DBGINFOFLAGS ?= -gdwarf-2 -gz
3888

@@ -73,10 +123,16 @@ endif
73123
# Assembler flags
74124
IOP_ASFLAGS := $(ASFLAGS_TARGET) -EL -G0 $(IOP_ASFLAGS)
75125

126+
endif
127+
76128
# Default link file
77129
ifeq ($(IOP_LINKFILE),)
130+
ifeq ($(IOP_USE_LLVM),1)
131+
IOP_LINKFILE := $(PS2SDKSRC)/iop/startup/src/linkfile.lld
132+
else
78133
IOP_LINKFILE := $(PS2SDKSRC)/iop/startup/src/linkfile
79134
endif
135+
endif
80136

81137
IOP_OBJS := $(IOP_OBJS:%=$(IOP_OBJS_DIR)%)
82138

@@ -102,9 +158,17 @@ $(IOP_OBJS_DIR)%.o: $(IOP_SRC_DIR)%.S
102158
$(DIR_GUARD)
103159
$(IOP_C_COMPILE) -c $< -o $@
104160

161+
ifeq ($(IOP_USE_LLVM),1)
162+
# For LLVM, use clang as assembler (requires -c flag)
163+
$(IOP_OBJS_DIR)%.o: $(IOP_SRC_DIR)%.s
164+
$(DIR_GUARD)
165+
$(IOP_AS) $(IOP_ASFLAGS) -c $< -o $@
166+
else
167+
# For GCC, use binutils as (no -c flag needed)
105168
$(IOP_OBJS_DIR)%.o: $(IOP_SRC_DIR)%.s
106169
$(DIR_GUARD)
107170
$(IOP_AS) $(IOP_ASFLAGS) $< -o $@
171+
endif
108172

109173
.INTERMEDIATE:: $(IOP_LIB)_tmp$(MAKE_CURPID) $(IOP_OBJS_DIR)build-imports.c $(IOP_OBJS_DIR)build-exports.c
110174

@@ -137,14 +201,30 @@ $(IOP_OBJS_DIR)exports.o: $(IOP_OBJS_DIR)build-exports.c
137201
$(DIR_GUARD)
138202
$(IOP_C_COMPILE) $(IOP_IETABLE_CFLAGS) -c $< -o $@
139203

204+
ifeq ($(IOP_USE_LLVM),1)
205+
# For LLVM, use clang as linker driver with lld
206+
$(IOP_BIN_ELF): $(IOP_OBJS) $(IOP_LIB_ARCHIVES) $(IOP_ADDITIONAL_DEPS)
207+
$(DIR_GUARD)
208+
$(IOP_C_COMPILE) -Wl,-T,$(IOP_LINKFILE) $(IOP_OPTFLAGS) -o $@ $(IOP_OBJS) $(IOP_LDFLAGS) $(IOP_LIB_ARCHIVES) $(IOP_LIBS)
209+
else
210+
# For GCC, use gcc as linker driver
140211
$(IOP_BIN_ELF): $(IOP_OBJS) $(IOP_LIB_ARCHIVES) $(IOP_ADDITIONAL_DEPS)
141212
$(DIR_GUARD)
142213
$(IOP_C_COMPILE) -T$(IOP_LINKFILE) $(IOP_OPTFLAGS) -o $@ $(IOP_OBJS) $(IOP_LDFLAGS) $(IOP_LIB_ARCHIVES) $(IOP_LIBS)
214+
endif
143215

216+
ifeq ($(IOP_USE_LLVM),1)
217+
# For LLVM, also remove LLVM-specific sections
218+
$(IOP_BIN_STRIPPED_ELF): $(IOP_BIN_ELF)
219+
$(DIR_GUARD)
220+
$(IOP_STRIP) --strip-unneeded --remove-section=.pdr --remove-section=.comment --remove-section=.mdebug.abi32 --remove-section=.gnu.attributes --remove-section=.llvm_addrsig --remove-section=.note.GNU-stack -o $@ $<
221+
else
144222
$(IOP_BIN_STRIPPED_ELF): $(IOP_BIN_ELF)
145223
$(DIR_GUARD)
146224
$(IOP_STRIP) --strip-unneeded --remove-section=.pdr --remove-section=.comment --remove-section=.mdebug.abi32 --remove-section=.gnu.attributes -o $@ $<
225+
endif
147226

227+
# Use IRX1 format for both GCC and LLVM (IRX2 not supported by PS2 loadcore)
148228
$(IOP_BIN): $(IOP_BIN_STRIPPED_ELF) $(PS2SDKSRC)/tools/srxfixup/bin/srxfixup
149229
$(PS2SDKSRC)/tools/srxfixup/bin/srxfixup --irx1 -o $@ $<
150230

iop/kernel/include/irx.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,34 @@ struct irx_import_stub
5959
/*
6060
* Ugly, yet functional.
6161
*/
62+
#ifdef __clang__
63+
/*
64+
* LLVM/Clang version: Generate entire import table header using inline assembly.
65+
* This ensures the header is emitted immediately before the stubs, preserving
66+
* the required layout for IRX format (LLVM lacks -fno-toplevel-reorder).
67+
* Structure: magic(4) + next(4) + version(2) + mode(2) + name(8) = 20 bytes
68+
*/
69+
#define DECLARE_IMPORT_TABLE(modname, major, minor) \
70+
__asm__ ( \
71+
".section .text\n\t" \
72+
".local _imp_" #modname "\n\t" \
73+
"_imp_" #modname ":\n\t" \
74+
".word 0x41e00000\n\t" \
75+
".word 0\n\t" \
76+
".hword ((" #major " << 8) | " #minor ")\n\t" \
77+
".hword 0\n\t" \
78+
"99:\n\t" \
79+
".ascii \"" #modname "\"\n\t" \
80+
".space 8 - (. - 99b)\n\t" \
81+
);
82+
#else
83+
/* GCC version: uses section name hack for compatibility */
6284
#define DECLARE_IMPORT_TABLE(modname, major, minor) \
6385
static struct irx_import_table _imp_##modname \
6486
__attribute__((section(".text\n\t#"), unused))= { \
6587
magic: IMPORT_MAGIC, version: IRX_VER(major, minor), \
6688
name: #modname, };
89+
#endif
6790

6891
#define STR(val) #val
6992
// .word 0x03e00008 == jr $ra (return immediately), this value will be patched later
@@ -96,11 +119,33 @@ struct irx_export_table {
96119
void *fptrs[];
97120
};
98121

122+
#ifdef __clang__
123+
/*
124+
* LLVM/Clang version: Generate entire export table header using inline assembly.
125+
* Export symbol must be global for RegisterLibraryEntries().
126+
* Structure: magic(4) + next(4) + version(2) + mode(2) + name(8) = 20 bytes
127+
*/
128+
#define DECLARE_EXPORT_TABLE(modname, major, minor) \
129+
__asm__ ( \
130+
".section .text\n\t" \
131+
".globl _exp_" #modname "\n\t" \
132+
"_exp_" #modname ":\n\t" \
133+
".word 0x41c00000\n\t" \
134+
".word 0\n\t" \
135+
".hword ((" #major " << 8) | " #minor ")\n\t" \
136+
".hword 0\n\t" \
137+
"99:\n\t" \
138+
".ascii \"" #modname "\"\n\t" \
139+
".space 8 - (. - 99b)\n\t" \
140+
);
141+
#else
142+
/* GCC version: uses section name hack for compatibility */
99143
#define DECLARE_EXPORT_TABLE(modname, major, minor) \
100144
struct irx_export_table _exp_##modname \
101145
__attribute__((section(".text\n\t#"), unused)) = { \
102146
magic: EXPORT_MAGIC, version: IRX_VER(major, minor), \
103147
name: #modname, };
148+
#endif
104149

105150
#define DECLARE_EXPORT(fptr) \
106151
__asm__ (".section\t.text\n\t.word\t" STR(fptr));

0 commit comments

Comments
 (0)