#MINTIA (not vibecoded)
1 messages · Page 16 of 1
which discards the realism and is geared for performance
and ill probably do that
i was kind of intentionally waiting to do that so that the "realistic" emulation would see performance work too
and i managed to get wayyyy higher with that than i anticipated
watch me make a jit and get 2x performance
kvm 
well duh
i wasnt counting that
2x? i expect at least 5-10x
especially for such an easily jittable thing
2x is like just unrolling a switch into asm calling into C
yeah idk i just said a random number
idk if libgccjit would be useful here tho
or something more low level
not sure but sounds like a cool project
either that or a TCC backend would be based af
for qemu
more like a tcg frontend
yeah
well qemu has been ported to so much shit I dont think it would be that hard
probs
mapping their ll/sc stuff over xrarch would be annoying
as on xrarch every other store causes the pair to fail i think?
u can sacrifice precision
thats also how it works on most architectures that have it
so id be surprised if they have no way to model it
on Alpha even device DMA will cause an ll/sc pair to fail
and they use this
extensively
i guess it just kills the reservation unconditionally?
the ll/sc pair will fail if the device does a DMA write to the cache line iirc
so its the same as if any processor did a store
on aarch64 i'm pretty sure the monitor is per cache line and not global
since if you disable caches ll/sc will fail indefinitely for example
what i meant by that is that if you do a normal store (ie not sc) it will still cause the ll/sc pair to fail
on at least alpha and mips
ah
and that was the preferred way to release a spinlock for example because it was faster than sc
only worked cuz itd cause the ll/sc pair to fail
actually no i dont think it matters in that specific case
Yea I don't understand that stuff so I'm not qualified to do a qemu port
I should learn more about that kind of cpu stuff
No I chose it when I was like 10
It's the buttons on an Xbox controller
ABYX
But rewritten
It's too late now to change my username I think
what is rustc
rust compiler
No it's the actual name of the rust compiler
yeah i remember now
crusty
does xremu work on Big Endian hosts
I'll run it on a Wii and report how many KHz it gets 
WARNING: This emulator does not make any attempt to be portable to big-endian host platforms! If you are on PowerPC for some reason, it will not run correctly!
in the readme
oh 💀
damn
so no
oh well
I can guess that the answer would probably be pretty low
last I checked, it runs at around 1/200th the effective speed of a Core 2 Duo E8400
just stick a le32toh around every read from memory and hope for the best :-)
lmao
NT can run on the wii
so...
i suppose you could theoretically just turn on LE for the emulator portion and then turn on BE for everything else?
are instructions byte-reversed in little endian mode?
yes but it can't really run anything useful
the chance that xremu builds with Visual C++ 4.0 RISC Edition for PowerPC is...... basically 0
and no other compiler exists for NT PPC
Who said you'd be running it on NT PPC
I said NT, a little endian OS, can run on the wii, so any little endian application could run on the wii
yes, you can turn on MSR[LE]
but then everything else breaks
Linux was never designed to work like that, it has 0 little endian PPC32 support
libogc is the same deal
except it'd be an even harder port because you gets basically nothing representitive of a real OS worth a damn
the question is, does turning on MSR[LE] require flipped PPC instructions
or is it the same byte format regardless
if not, then it shouldn't be that hard to just turn it off before every syscall
if it is, then it's actually pretty difficult, i agree
one sec I can check
yes it would appear to be
same bytes are interpreted different in BE vs LE
you also need supervisor access to flip modes
There was a bug in early discord like 10 years ago in 2015 where the "X is typing..." indicator at the bottom of a chat could get jammed on and say that for weeks straight even if they typed something else
Often channels would just accumulate these over time until they constantly said "several people are typing..." at all times
Sometimes I miss that.
It's moderately difficult afaik, you can look into avr target in target/avr and hw/avr - there's a couple thousand lines of code and from a glance the arch is super simple
translate.c does most of the heavy lifting
i would've gone insane
try again again
this time build it with make FASTMEMORY=1
i now get this on my m1 macbook air lol
its executing 0.7-0.8 guest BIPS
706880126 cycles per second
704879603 cycles per second
703879696 cycles per second
703879239 cycles per second
704879466 cycles per second
702879764 cycles per second
702880129 cycles per second
i declare the emulator saga closed
i brought it up from 40mips peak to 700mips
and when its only doing 20mips it now only uses like 30% of one efficiency core on my m1 mba. it used to use 50% of a performance core
on my shitty 2015 intel imac it uses 10% of a core while doing 20mips
damn very nice
now that this saga is over i can return to mintia2 io system
in like idk a few months whenever i have a break
lolz
Is this in a special mode or full accuracy?
Cache sim is disabled but all functionality is present
Its an alternate memory subsystem optimized for performance
Rather than realism
The original realistic one is still pretty fast on its own, 300 MIPS
or so
Ah cool
oh right yep one second my apologies i will hop to this rn
VERY big number
awesome sauce!!!!
wow this is like amd athlon 64 speeds now
didnt that have a base of 1ghz
blisteringly fast
~965 MHz
HUGE
we can just round that up and claim 1 BIPS on your machine
or I can just test on a 11th gen i7 and try to get bigger number
I managed to make paging incredibly speedy is where most of that came from
I used a very very neat trick though I do say so myself
hmmm this is on wsl, i'll try to see if i can get it natively running
should work fine
I don't think that would do anything
that's probably just as fast as it runs
When you get to speeds like that it gets extremely sensitive to cache configurations and stuff
So there's probably just something unlucky happening on chip
I'll hop back over to my mac and see if i can get good rng over there
Some extremely hot cache lines thrashing a cache set or something like that
By pure chance
Of how the cache on that particular chip is laid out
If the exact bottleneck was discovered it could be changed but then there'd be some other rng bottleneck on other machines
It's like trying to flatten a carpet and every time you flatten the bulge it just pops up somewhere else
I'll cache align things and see if that does anything
__attribute__((aligned(64))) my beloved
Could help
The hot structures are in xr.h
In particular some fields in XrProcessor and also the structures XrIblock (which caches a run of decoded instructions) and XrCachedInst (which holds a decoded instruction) and stuff
Play around and if you find something that gets a consistent improvement I'll try it out and potentially merge
this is what i get on native linux (ryzen 5800X)
About 600 MIPS
Highest so far is 1 BIPS on gummi's machine. Second highest is 700 MIPS on my m1 MacBook Air
Is that with FASTMEMORY=1 btw
yeah
oh not latest commit, it's from like 1 or 2 days ago i think
i'll try latest commit 2s
oh btw the cmdline i'm using is ./graphical.sh -dks mintia-XRstation-fre.img -threads 16 -cpuhz 4000000000, anything obvious i should change there?
Just do a single thread
There's only 1 cpu anyway
UI is still in a separate thread
Old mintia doesn't support smp so adding more CPUs will be ineffectual, it doesn't see them and can't use them
It predates SMP actually
this is with latest commit CC=clang FASTMEMORY=1, -threads 1
Another unlucky case I guess
I hate cache rng lol
Which I'm assuming is causing the seeming lack of correlation between host processor speed and max performance of this emulator
whether the host cache happens to be configured such that it plays nicely with the emulator may be a much greater factor than anything else
In any case that's far faster than it even needs to run lol
ok for some reason it now starts giving 700MIPS whereas it was doing 920MIPS prior, and I thought it was because I did the attribute aligned, but I removed that and it was the same
It overflowed
ahhh
note to self don't do 20M runs
Downside of 1-to-1 translation of a benchmark program that was popular in 1986
nvm it's back
I will try to cache align a few things instead of everything then lol
Packing it in the cache I suspect is more beneficial than cache aligning
In this case
But idrk and u can try
this is remarkably speedy
Yeah I figured out how to make accesses with paging enabled extremely quick
It inlines to like 15 instructions in the fast path
Inlined into the execute routines for the load and store instructions
That gave most of the speed up
cache aligning the icache and dcache in the emulator helped a bit
consistently better by a bit
But the icache and dcache are compiled out entirely when FASTMEMORY=1
hmmm i guess this is rng again
1 BIP is beyond my wildest dreams when I set out
I expected 2-6x improvement
I got 18x at least
Job is done
Staticsaga guessed the theoretical limit for a cached interpreter at around 340 MIPS lol
More than doubled that so I'm happy
@safe hollow @acoustic sparrow this guy got ~960 guest MIPS on my emulator
Can we round that up to 1 BIPS
What cpu does that have btw
Anyway I have to sleep bye
It very well could
I consistently get better performance on my Intel iMac if I pause my music while I run dhrystone lol
The system audio threads take enough cpu time to notice on the dhrystone results
this is what i get with nothing else running on the system and taskset -c 0
ryzen 5800x too wow
for reference previous numbers were with like 15 firefox tabs open, one of which doing video playback, and my system's file indexer running in the background
could not get higher than this on my machine
well I guess That's The Run then
inb4 abbix tells us all how they could get xremu to run 5 morbillion times faster than the host CPU with a JIT
nah a JIT is pretty much a gimmick at this point
But it'd be fun to do
Do you concede defeat after I got 0.7-1 BIPS without one
you're lucky that the architecture is already pretty optimized
cause instructions are fixed 4 bytes etc
it's not really luck tho 
What is a VAX MIPS
btw what's the state of your Linux port atm
It's in units of the performance of a VAX-11/780 which was marketed as a "1 MIPS machine" in 1978 (insane stuff back then)
GCC can compile empty files. I'm working on empty functions rn
In practice it was about 0.5 real MIPS lol
xr gcc...
I hope this is at least educational for you lol
Retargeting all this stuff
Oh yeah definitely
write a jackal-to-C tool and rewrite mintia in C
and get free perf boost with gcc optimizations
the actual jackal compiler has a c backend
it's how jackal-written host tools are compiled
ohh right
I thought it was lua
like it started off with lua
very interesting 👍
Real hardware xrarch linux when
they got close with the real hardware part
someone half implemented it in verilog for an fpga
it got far enough to reach the boot console
didnt pitust get it to work using a baremetal emulator
@icy bridge i can't get your binutils port to compile, i'm not sure what happens but when i run jinx host-build binutils it eventually dies with an error 2, if you are interested here's the output: https://gist.github.com/48cf/754bac551becd08457104cb705629b2b
/base_dir/sources/binutils/gprofng/libcollector/configure: line 18421: syntax error near unexpected token `-Wno-nonnull-compare,'
/base_dir/sources/binutils/gprofng/libcollector/configure: line 18421: `ACX_PROG_CC_WARNING_OPTS(-Wno-nonnull-compare, GPROFNG_NO_NONNULL_COMPARE_CFLAGS)'```
it looks like it's parallelizing it incorrectly or something, it's weird
hm
oh i didn't see that :^)
can you send sources/binutils/gprofng/libcollector/configure.ac and sources/binutils/gprofng/libcollector/configure?
i want to diff them with my versions
Dragonfruit compiler was written in luer
So was the old assembler and linker
what was the bootstrap jackal compiler written in
But all are now in jackal
$ sha256sum configure configure.2 configure.ac configure.ac.2
caf82c618b3aaec7d038a25523bd202439b15a76f1bbfbbe78e0ffc6b07d5bc9 configure
caf82c618b3aaec7d038a25523bd202439b15a76f1bbfbbe78e0ffc6b07d5bc9 configure.2
71ddf5f329b89c1e4e0e33114f021ed3758aa341be6f3b324a3859d6602eddbf configure.ac
71ddf5f329b89c1e4e0e33114f021ed3758aa341be6f3b324a3859d6602eddbf configure.ac.2``` well then
that isn't the issue
Lua
ouch
writing a bootstrap compiler in an "easy language" is the way
yeah i have no idea what's wrong honestly, maybe you could jinx host-rebuild binutils and see if you can reproduce?
or even better
don't mess up your build and just do it on a fresh clone :^)
This was the bootstrap compiler
https://github.com/xrarch/sdk/tree/master/tbc
i literally just cloned it and that's what i get, i also tried building it few days ago on a different system and it failed in the same way
It converts an early form of jackal to C basically
no preprocessor support other than includes
The whole jackal compiler was written with it before it could compile itself
btw @icy bridge is the autoconf patch supposed to say UNAME_MACINE? looks like a misspelling of UNAME_MACHINE, it's on line 9 of the patch
It now uses features not implemented by the bootstrap compiler but only by itself so the bootstrap compiler cant compile the real compiler anymore. It stands fully on itself and if all functional jackal compiler binaries disappeared from the earth id be in BIG trouble
considering the patch context includes lines for xtensa linux which reference UNAME_MACHINE i am guessing it's wrong :^)
oh yeah that's a typo
Likewise the build tool uses itself as its build system
So the toolchain is fully self contained
No OS project is complete without a self hosting toolchain developed alongside it
Just ask Linux and NT
(and almost all OS projects between 1965 and 1985)
I think self hosting a toolchain is among the 10 most satisfying things you can do with a computer
@sterile frost cannot reproduce
wtf
mkdir build; cd build; ../jinx init ..
also fair warning, the binutils port is functional but not complete
in particular the assembler:
- does not emit any warnings or errors when values don't fit in immediate fields
- does not implement any pseudo instructions
- does not infer immediate versions of instructions if the non-immediate mnemonic is used with an immediate operand
iretq@DESKTOP-TAQSOFJ:~/xrlinux/build$ ls -la host-pkgs/binutils/usr/local/bin/
total 81236
drwxr-xr-x 2 iretq iretq 4096 Aug 19 17:57 .
drwxr-xr-x 6 iretq iretq 4096 Aug 19 17:57 ..
-rwxr-xr-x 1 iretq iretq 4669432 Aug 19 17:57 xr17032-unknown-linux-gnu-addr2line
-rwxr-xr-x 2 iretq iretq 4843960 Aug 19 17:57 xr17032-unknown-linux-gnu-ar
-rwxr-xr-x 2 iretq iretq 6298872 Aug 19 17:57 xr17032-unknown-linux-gnu-as
-rwxr-xr-x 1 iretq iretq 4617936 Aug 19 17:57 xr17032-unknown-linux-gnu-c++filt
-rwxr-xr-x 1 iretq iretq 117488 Aug 19 17:57 xr17032-unknown-linux-gnu-elfedit
-rwxr-xr-x 1 iretq iretq 5229408 Aug 19 17:57 xr17032-unknown-linux-gnu-gprof
-rwxr-xr-x 4 iretq iretq 7844976 Aug 19 17:57 xr17032-unknown-linux-gnu-ld
-rwxr-xr-x 4 iretq iretq 7844976 Aug 19 17:57 xr17032-unknown-linux-gnu-ld.bfd
-rwxr-xr-x 2 iretq iretq 4734624 Aug 19 17:57 xr17032-unknown-linux-gnu-nm
-rwxr-xr-x 2 iretq iretq 5454240 Aug 19 17:57 xr17032-unknown-linux-gnu-objcopy
-rwxr-xr-x 2 iretq iretq 7818920 Aug 19 17:57 xr17032-unknown-linux-gnu-objdump
-rwxr-xr-x 2 iretq iretq 4843992 Aug 19 17:57 xr17032-unknown-linux-gnu-ranlib
-rwxr-xr-x 2 iretq iretq 4041240 Aug 19 17:57 xr17032-unknown-linux-gnu-readelf
-rwxr-xr-x 1 iretq iretq 4658552 Aug 19 17:57 xr17032-unknown-linux-gnu-size
-rwxr-xr-x 1 iretq iretq 4681552 Aug 19 17:57 xr17032-unknown-linux-gnu-strings
-rwxr-xr-x 2 iretq iretq 5454240 Aug 19 17:57 xr17032-unknown-linux-gnu-strip```
perfect :^)
What'd you think of the ISA you're one of the few people who has looked at it that deep
I think the weirdest thing about it is that all the load instructions zero extend and there's no sign extending versions so you have to left shift and arithmetic right shift for sign extension
That's the opposite of typical and is basically by accident
But there is a reason which is that people usually write C programs with tons of signed types and so that's what ends up usually being fastest which is why loads are always sign extending on like MIPS. but df and jackal programs tend to use almost all unsigned types
it's pretty nice in general, the two things that mainly stand out to me are:
- regular jumps/calls (not indirect or beq zero, <target>) having absolute targets instead of pcrel ones is weird imo. for jumps this can be mitigated by using beq zero, but there is no equivalent way to get a pcrel call. this fucks up ELF dynamic linking because it's impossible to get your own pc and means pre-relocation code (aka the code that relocates the dynamic linker) cannot involve any function calls
- the comparing branch instructions don't have signed versions
also pcrel targets for j/jal would mean the weird "upper bit of target is upper bit of pc" thing doesn't have to exist
but that's not a super big deal
I could add an auipc like instruction
Which resolves this
i should still look into why my patches break compiling binutils for the normal target
You can use that combined with the immediate offset on jalr for relative calls
That's how riscv does it
Would that be helpful
yeah that'd work
Yaoi pc instruction
added a basic one just now
very nice, thanks 🙏
I also might make the immediate offset on jalr shifted left by 2, it isn't rn which is a little inconsistent
I don't think I use that immediate offset on jalr literally ever
So I don't think that'd break anything lol
So what does it mean exactly
It's fully possible that it's the emulator's implementation that's wrong. It's not like I'd ever notice
Lmao
actually now that i think about it this doesn't really work because that would have a granularity of 64K while relocation has a granularity of 4K (page size)
Wdym granularity of 64k
It has a byte granularity
auipc presumably only loads the top half of the register
that's what it does on riscv at least
No it shifts the immediate left by 16 and adds it to pc and stores the full result in the register
ah ok
Then the immediate offset on jalr provides the lower 16 bits
yeah nevermind that'd work then
So this instruction pair does a full 32+32 add of pc+immediate
if it were a perfect world i'd prefer j/jal being made pcrel instead of (or in addition to) auipc being added but that'd break abi
The one followed by the like 200kloc of guest code that runs currently
Wdym what abi
also is there a reason why j and jal are two different actual instructions instead of j being a pseudoinstruction for jal zero, x
Range
j and jal have the same range though?
i misread the message sorry...
Yes
they're both (pc & 0x80000000) | (imm29 << 2)
wouldn't that simply be an assembler change though
This would have a smaller range than J and JAL
They have 29 bit immediates
You're suggesting I encode a register number in JAL
oh wait yeah that'd need a new register number
That takes up 5 bits
Also this doesn't help at all because that only speeds up decoding which is what I'm caching anyway. What actually helps is that the icache is incoherent so I don't need to watch for writes into cached basic blocks cuz every time the instruction stream is dynamically modified there's a manual icache flush which I can hook for basic block cache flushing
the main reason i'd prefer this is because then i'd have a one-instruction pcrel call, whereas with the auipc-jalr thing i'd need to either use two instructions per call everywhere (inefficient) or add a custom attribute to every c function that needs to be called pre-relocation (annoying)
Also your suggestion wouldn't cover the entire 2GB of kernel and user space
Which was the nice property I liked
is this the jal zero, x suggestion or the pcrel one
pcrel
yeah fair enough
It'd only cover +-1GB from current pc
This works better with my dynamic linking scheme which is more like PE than ELF
yeah fair enough
I considered stuff I just made different trade offs from the ones linux would prefer sometimes
Cuz I was running notlinux
The zero extended loads are an example of optimizing for running not-C as well lol
now that i think about it some more, the fact j/jal are absolute means that things that use them for calls need relocations in .text, which is illegal in static-pie (aka self-relocating code (aka the dynamic linker)) so i might as well just make it a compiler option instead of an attribute
does xr3278143128 have some way to sign extend?
sorry i dont know the numbers
xr10374
xr17032
close enough 
pretty sure riscv's calls are also absolute
got one digit wrong and in the wrong order
they aren't
Well MIPS's definitely are
they have the jal x, target thing
where target is pcrel
and j is a pseudoinstruction
MIPS definitely has absolute jal target
alright i'll take a look
This option is also always available
You can generate calls with the auipc jalr sequence always if something is compiled as PIE
I can make my emulator do fusion on this pair doing funny cached interpreter tricks so it's not any slower
mips jal is relative
Can't say the same for a real in order chip implementation
This is a PC-region branch (not PC-relative); the effective target address is in the “current” 256MB-aligned region huh this really is just xr17032's j/jal except a smaller region
Nuh uh
Yeah that
SPARC's calls work similarly as well
ah wait yeah, its not pc relative its just within the same region as the pc
if i'm reading godbolt output correctly (not guaranteed since i have 0 experience reading mips asm) this is exactly what happens on mips
can you show me
I have a gap between classes today and I'll use that to clean up the documentation mistakes you pointed out and probably implement auipc
sounds good
Also I think what I thought was inconsistent was the jalr immediate being left shifted by 2 cuz that's inconsistent with the other arithmetic immediates and is also unnecessary because auipc provides a whole upper 16 bits anyway
let me ask again but this time let me actually be respectful: does xr17032 have a way to sign extend a value?
So rather than making the emulator left shift it, I might change the specification for it to lack the shift
Yes, left shift and then arithmetic right shift
interesting, thanks
I also might make the immediate not be sign extended
well sure it's inconsistent with the other arithmetic instructions but it's not the only place where the assembler needs to take into account a 16-bit immediate that's left-shifted by 2 (mov long [ra + imm16] has it as well) so making it not left shifted doesn't reduce complexity at all
True enough, I think the sign extension is unique though
it is yeah
i have a simm16_2 operand type specifically for jalr
making it zero-extended makes sense but imo removing the left shift doesn't
Wdym, just adjust the compiler and recompile it, why is that a problem?
Because I say so
Also I thought u only had like a demo userspace that couldn't do much
that's mintia2
old mintia is fairly complete and there are other oses targeting xr17032
What sort of apps did it have?
Also I don't want to make every person who ever downloaded the emulator be confused why everything broke suddenly
Breaking changes are just annoying
here's what my local copy of it has
Damn
Basically command line tools
Plus it had like a userspace server or two
It had a userspace server for like logon and user management
amssrv.exe
But don't remind me how much work I discarded
also this is an extension of the operand being sign-extended thing but i don't think there's any one-instruction sequence for bitwise not? normally you'd do xori ra, rb, -1 but that doesn't work on xr17032 because the operand is zero-extended
There is
Look at the not pseudoinstruction
Guh
i don't see it in XrRegisterKeywords either
It uses NOR
ohh nor ra, rb, zero
makes sense
i was wondering why there was a dedicated nor instruction, that's not a particularly common operation
I may be stupid and actually just always type the nor thing manually
progress on gcc port: $ echo 'void _start(){}' | host-pkgs/gcc-bootstrap/usr/local/bin/xr17032-unknown-linux-gnu-gcc -x c -S -o /dev/stdout - .file "<stdin>" .text .p2align 2 .globl _start .type _start, @function _start: subi sp,sp,8 mov long [sp + 4],s0 mov long [sp],lr addi s0,sp,8 addi zero,zero,0 mov s0,long [sp + 4] mov lr,long [sp] addi sp,sp,8 jalr zero,lr,0 .size _start, .-_start .ident "GCC: (GNU) 15.2.0"
what happens if you run with -O3?
internal compiler error because i haven't defined instructions for some operations yet, but here's -O1 $ echo 'void _start(){}' | host-pkgs/gcc-bootstrap/usr/local/bin/xr17032-unknown-linux-gnu-gcc -x c -S -o /dev/stdout - -O1 .file "<stdin>" .text .p2align 2 .globl _start .type _start, @function _start: jalr zero,lr,0 .size _start, .-_start .ident "GCC: (GNU) 15.2.0"
and -O0 -fomit-frame-pointer: $ echo 'void _start(){}' | host-pkgs/gcc-bootstrap/usr/local/bin/xr17032-unknown-linux-gnu-gcc -x c -S -o /dev/stdout - -fomit-frame-pointer .file "<stdin>" .text .p2align 2 .globl _start .type _start, @function _start: addi zero,zero,0 jalr zero,lr,0 .size _start, .-_start .ident "GCC: (GNU) 15.2.0"
compiling into an object file also works: ```
$ host-pkgs/binutils/usr/local/bin/xr17032-unknown-linux-gnu-objdump -d test.o
test.o: file format elf32-xr17032
Disassembly of section .text:
00000000 <_start>:
0: b4 f7 08 00 subi sp,sp,0x8
4: aa 5f 01 00 mov long [sp + 0x4],s0
8: f9 07 1e 90 mov long [zero + sp LSH 0],lr
c: fc f2 08 00 addi s0,sp,0x8
10: 3c 00 00 00 addi zero,zero,0x0
14: eb f2 01 00 mov s0,long [sp + 0x4]
18: f9 07 1e d0 mov lr,long [zero + sp LSH 0]
1c: bc f7 08 00 addi sp,sp,0x8
20: 38 f8 00 00 jalr zero,lr,0x0```
how good is gcc's per arch abstraction?
it's decent ig? there's still a decent amount of stuff that's effectively duplicated (for example i had to "write" (read: copy from riscv) stack layout computation, prologue/epilogue generation, that kinda stuff)
kind of on a tangent here but where did you learn how to port gcc and binutils to a new architecture? I've always been curious of the process
you would think after porting it to 9999 arches it would be streamlined
for both of them: i googled it, got to some generic porting guides, and ignored everything they said except for what files to edit to get it to accept the target triple
from there i just kept compiling and fixing whatever error i got until it worked
same with runtime errors really
would be funny to add a jackal frontend lol
all of a sudden the entire mintia would probably get optimized out
a very-long-term goal is to add a xr17032 backend to llvm as well then build a jackal compiler on top of that
damn
i don't think it'd be able to compile mintia2 though
it probably relies on the compiler being primitive in a lot of places
i've come across a few places in that codebase where it makes some assumptions about compiler codegen behavior that llvm would definitely violate
yep
for example backtracing
well at O0 might work
thats some funny codegen but the assembler accepts it so who cares
for reference this is the specific error (it's the same for O2 and O3) $ echo 'void _start(){}' | host-pkgs/gcc-bootstrap/usr/local/bin/xr17032-unknown-linux-gnu-gcc -x c -S -o /dev/stdout - -O2 .file "<stdin>" .text during RTL pass: compgotos <stdin>: In function '_start': <stdin>:1:15: internal compiler error: in invalid_rtx, at ./insn-target-def.h:48 0x172d0b1 internal_error(char const*, ...) /base_dir/sources/gcc/gcc/diagnostic-global-context.cc:517 0x60ecc1 fancy_abort(char const*, int, char const*) /base_dir/sources/gcc/gcc/diagnostic.cc:1749 0x603006 invalid_rtx ./insn-target-def.h:48 0x14d79db get_uncond_jump_length() /base_dir/sources/gcc/gcc/bb-reorder.cc:1408 0x14d7adc duplicate_computed_gotos /base_dir/sources/gcc/gcc/bb-reorder.cc:2769 0x14d7adc execute /base_dir/sources/gcc/gcc/bb-reorder.cc:2836 Please submit a full bug report, with preprocessed source (by using -freport-bug). Please include the complete backtrace with any bug report. See <https://gcc.gnu.org/bugs/> for instructions.
not actually sure what operation it wants but i'll figure that out some other time
damn RTX in gcc 
ahhh I see
Not as much as you'd think?
In fact not really at all
offsets aren't sign extended so im not sure that'll work
oh right
that's an issue
actually I wonder why that was accepted at all, the constraints I put on mov shouldn't allow negative immediate offsets
This is fair but also highly intentional and not difficult to rectify by using some other debug info rather than the thing I do currently
But like for example im careful to use compiler fences and stuff to prevent loads from being optimized together that should be real loads and so on and so forth
Which should at least be a good start
Yeah I don't think it'd be a massive undertaking to adapt it to llvm codegen or anything like that but it definitely won't work unchanged
What other examples can you think of aside from the back tracing
Nothing concrete off the top of my head, I just remember thinking "oh this seems like it relies on codegen details" at times when looking through it
slander!
This seems like it could single handedly make your work way more ass pain so I apologize
Ehh I don't think it'll be a huge issue
Especially cuz there's no real reason for it to be zero extended it has just always been that way
I can just make it so the frame pointer (s0) points at the lowest address of the frame instead of the highest, then all stack accesses should have positive offsets regardless of -fomit-frame-pointer presence
gcc has to support other architectures that have only unsigned offsets
lowest towards zero or lowest towards the bottom of the stack
And GCC's generic codegen stuff should be able to deal with the rare cases where negative offsets really are necessary by spilling the offset into a register
Towards zero
Yeah I looked at the code and it seems like I just forgot a single < 0 check in the "is this a valid memory operand" callback
Yes
That's good
@twilit smelt is there any detailed documentation on the call convention that the jackal compiler uses? e.g. what register contains the return value (a0?), how are arguments and return values >32 bits handled (stack? 2 registers if <=64 bits, stack otherwise? etc), how are the stack locations for stack-passed arguments determined (lowest address for first stack argument, with each argument aligned to 32 bit boundary?), etc
are you hoping to be ABI compatible
yes
return value is in a3, OUT parameters are subsequent return values and are passed out in a2, a1, a0 in descending order and after that on the stack
arguments and return values >32 bits are not handled you cant do that in jackal
you can only deal in machine words
ah okay
so on a 64 bit machine you could do 64 bit but not 128 bit
that makes things easier because i can effectively make up whatever i want
for stack passed arguments and return values, thats a good question
i dont remmber
you may need to toy with writing little jackal programs to see what it emits
fair enough
oh and for arguments <32 bits does the callee expect the upper bits to be zeroed, sign extended, garbage..?
NOTESFORSPEC in the jackal compiler codebase may be illuminating, contains notes on all kinds of weird junk i wanted to keep track of for when i wrote a spec (never happened (yet))
including what you just asked
alr i'll take a look at that
ah ok
thats quite cursed ngl
does point 6 mean that for signed args/return values the upper bits need to be sign extended or does it only apply to locals
i didnt really understand what it meant by the wrapping bit
so if u do u8 x = 0; x += 256 what will it contain
the register/stack slot it's allocated to will contain 256 but the code is generated in a way where it acts as if it contains 0
that's how i interpret it at least
so its basically saying there's no extra code to mask the value or whatever
yea
oh
mad?
i do have to admit the whole "int is 16 bit" part tripped me up a bit when writing the assembler
me when i learned about GAS .long
i can assume
on amd64, 64 bit arithmetic is slower
because it uses more bytes
idk where i got that from, ive been using byte/int/long nomenclature for 8/16/32 since 2018
its probably an artifact of me being 15 when i started
and just doing dumb things
tbf that is the specified minimum for C's basic types
char is min 8, int min 16, long min 32
maybe thats where i got it then
is there an add with carry for 64-bit math?
you can compute the carry in one instruction anyway
no you have to do the mips type sequence with slt, which jackal compiler can generate from this
#MACRO RtlAddUquadToUquad ( destquad, srcquad ) [
srclow := (srcquad)^.Low
low := (destquad)^.Low + srclow
NOTHING (destquad)^.Low = low
NOTHING (destquad)^.High += (low < srclow) + (srcquad)^.High
]
kinda silly that your compiler doesnt have u64 on 32 bit platforms too
@icy bridge btw theres already a (planned to be) much better compiler with a jackal frontend being designed/written rn by sandwichman, called coyote. it uses their "iron" compiler infrastructure theyre developing for their own fantasy computer Aphelion
if im a kernel autist, sandwichman is a compiler autist
so those will probably be the respective strengths of our projects lol
and its not like the extra masking is expensive, you can do some tricks to do it with little penalty
ah okay
their frontend can already parse the entire OG jackal parser (which is like 5000 lines)
is that compiler capable of passing >word-size arguments/return values? if so i'll probably try to make my gcc port use their calling conventions for what your jackal compiler doesn't support
or have they not gotten to that point yet
well, its not actually to the point of generating code from jackal programs
theyre doing a weird outside-in development where theyre doing the frontend and the backend at the same time (using hand-written IR to test the backend) and filling in the middle
lol
the middle isnt filled in yet
but theyve parsed complicated jackal programs and generated xr17032 assembly
they just havent translated jackal to xr17032 assembly
yet
neat
that's neat yeah
but also the middle is like 99% of the work
but for call convention purposes it's the ir-to-assembly stuff i need to be compatible with
unless their ir only deals with words?
yeah i think they did that to mitigate boredom probably (cuz it can be boring to work front-to-back and it takes forever to get any results, so it might be a little less boring to do the front and back and then the middle)
rather than for any like methodology reason
idk tho
no idea
@lucid umbra can comment more
they have been doing their own jackal language extensions
im unsure the full scope
none of it has been backwards incompatible
just adding QoL stuff the old compiler doesnt allow (including potentially >word size stuff)
tbf im working on my own compiler meme (alongside 3 other projects lol) and rn im on spill slot allocation
i'm looking at the src for iron rn and it does seem like their ir has types but i can't find any code dealing with call conventions
yeah it might just not be there yet
they took a break from it to work on their own architecture
theyre polishing up their ISA manual rn
fair enough
i told them ill switch to their compiler once its self hosting (which will occur after they rewrite it in their language it is primarily meant to compile, mars) and can build mintia2 and my linker and assembler
cuz its likely to be way better than mine
im bad at comper
good at kernel
they bad at kernel good at comper
so theres a mutually beneficial arrangement to be had here
it might help if you wrote a typed IR lol
you are so sassy
unless they already have something else and i just can't find it i'll probably go with this
- arguments: <= 2 words in registers if possible (no splitting between registers and stack), >2 words always on stack, successive stack arguments have increasing addresses, each argument aligned to 32 bit boundary
- return values: <= 4 words in registers (so jackal functions with out arguments can be "translated" by making the c signature have a struct as return type), >4 words written to pointer passed as first argument
oh also keep in mind jackal has varargs stuff
is that like with most c abis where varargs have the exact same call convention as normal arguments?
but i think the way jackal does it doesnt play nice with the way C does it so you might just decide those arent directly compatible
no
it compiles to storing the varargs in a stack-allocated array and then a pointer to the array is passed as a virtual argument
along with a count
of the number of elements in the array
oh that's very different from c varargs yeah
Wait so you are handrolling an ABI?
i did for my custom architecture yes
and custom language
kind of have to
at that point
Custom architecture?
i think it's best for gcc to not do that on a compiler level and instead make the actual code deal with that
like void some_jackal_function_with_varargs(void *args, size_t count)
If you want an actual compiler that can compile useful software; consider adding a new LLVM backend
and have a separate abi for c varargs
There is literally a tutorial on how to do that
yes
im doing my own kernel as well in my own lang
with its own native 100% NIHed userland
so thats not super useful to me
i mean i'm writing a gcc backend for it literally as we speak
monkuous is porting linux rn though
on that note i fixed the -O3 compiler crash (it was missing the instruction definition for unconditional jumps) $ echo 'void foo(){}' | host-pkgs/gcc-bootstrap/usr/local/bin/xr17032-unknown-linux-gnu-gcc -x c -S -o /dev/stdout - -O3 .file "<stdin>" .text .p2align 2 .globl foo .type foo, @function foo: jalr zero,lr,0 .size foo, .-foo .ident "GCC: (GNU) 15.2.0"
I'm fairly sure that LLVM should be easier to make a backend for
okay do it then
I have no idea how the CPU architecture works
they already decided on gcc
So you'll need to make documentation then
or you can go away youre annoying me
yeah but gcc is required for an lsb-compliant linux userspace (it requires libgcc_s.so)
making an llvm backend is another future goal though
Gentoo Packages Database
There is a libgcc replacement
For LLVM
Ignore the fact its a gentoo package
hm
well still
i've already gotten most of the hard work in writing a gcc backend done
at this point it's mostly just implementing the call convention hooks and adding the various instruction definitions
writing an llvm backend is probably easier than a gcc backend but probably not easier than finishing my current gcc backend
Btw how does the CPU architecture work
As in; is it RISC or CISC
Or EPIC
RISC
mkay
quite possibly
LLVM is literally designed to run anywhere
but llvm is better anyway
can you build llvm with gcc
yes
Yes
ofc
LLVM is quite easily able to be built with any semi-recent GCC/LLVM
Any C++20 compliant compiler is fine
it has some properties that aren't common for RISCs (most things are zero extended instead of sign extended, most arithmetic operations can also apply an immediate shift operation on one of the operands, memory operands have SIB) but for the most part it's a pretty normal RISC
OH GOD
Zero extension is hell
riscv and arm both have SIB on memory ops afaik
riscv just has base and an immediate offset
Most C software is built with the idea of sign extension a lot of the time
i think theres an extension that adds proper indexed addressing but idk
(int32_t)(int8_t)-49 should be (int32_t)-49
can't be that bad considering amd64 does zero extension for the most part as well
Not 207
i did it differently because i had a codebase that prefers zero extension
keep in mind i already have like 200k lines of non-C code
running on this thing
C rules and conventions do not necessarily apply
you'd have to have a really bad C compiler to make "does the architecture zero extend by default?" affect the actual code
Cries in AIX's C compiler
sure on most riscs that operation's a noop but just because it isn't on xr17032 shouldn't affect the code's behavior
K
hell even on x86_64 that isn't a noop
also this needs to be remembered
Non-C code?
i feel like you took this as a fledgling baby project when you offered advice but theres already a large mass of stuff running on it, its a 7 yr old project
its not particularly useful but it sure is large
custom languages
Damn
Hand-rolled compiler?
yeah
What backend does it use
custom
theres a language called jackal which has a fully NIH self hosting compiler
its not a particularly good compiler but its there
whats your aliasing model 
pee
Hmm
I'm sure that if you use GCC/LLVM with a custom backend; you could get more performance
also i dont think that applies because locals cant escape anyway
right
Considering these have shitloads of optimizations built-in
other than explicitly stack allocated stuff
it does for pointers
this is very obvious
the goal isnt performance
its to be 100% NIHed
for no particular reason
other than its cool
see the smp scheduler for the kernel for example https://github.com/xrarch/mintia2/blob/main/OS/Executive/Ke/KeDispatch.jkl
@twilit smelt also are you considering implementing the CPU core on an FPGA using Verilog/VHDL
yes thats a long term goal
someone already implemented enough of it on an fpga to run the boot rom prompt
no mass storage or mmu though so it couldnt boot the OSes
Just implement a QSPI PHY
That should work
You can also consider implementing an USB PHY and using that
"just"
it can't be that hard :^)
There are prebuilt USB PHY's
yeah ik
for usb ls/fs it seems pretty easy from what i've seen
Yea it is
Its not really hard
You don't even need an expensive FPGA
Just a cheap one will suffice
i tend to not know anything about anything until i do the thing
and then i know lots of stuff
but i have to do the thing
thats a thing i havent done and dont plan to until well after the OS has a sizable userspace
(again)
so itll be some time until any of that is relevant to me
and ill forget everything youre saying rn
qspi phy should be simple too, and should only need 1 extra pll output for a 180 deg phase shifted clock
@icy bridge oh crap i forgot to add the yaoi pc instruction
and fix the jalr sign extension thing
its fine i havent gotten to the point where that matters yet
(aka i havent implemented pic yet)
the manuals are actually in kind of a messed up state on my end because i got like halfway through an aborted restyling and i stupidly did a bunch of content changes inline with that cuz im an idiot
and now i need to go through and merge the good content changes into the old styled stuff
which is gonna be really boring and annoying so ive been stalling
any architectural changes are kind of blocked on me doing that
well, i guess i can change the old manuals as they exist in the books repo and then the process of merging will just be a little more complicated lol
hoped to avoid that though
heres my immediate todo list:
- finish up most recent emulator polishings
- finish replacing pre-rendered png diagrams in the manuals with new svg versions
- merge good content changes (including new diagrams) into old-styled manuals
- integrate monkuous comments (mistaken hex opcodes and other mistakes) into manuals
- specify auipc instruction and remove sign extension from jalr immediate in manuals
- implement 5 in emulator
@icy bridge if i dont finish these by next weekend you may force feed me drano
as im sure youll want to due to the pure hatred my failure will create in your heart
i might call it something like adr rather than auipc because the former is what arm calls it and the latter is what riscv calls it
and i dont want to be associated with riscv.
🤮
so it'd be ADR RA, IMM21 (RA = PC + (SignExtend(IMM21) << 11))?
something like that
well
idk why youre sign extending the immediate
just to shift the bits you extended
out
clarity but yeah that's redundant
it bothers me to have a special branch format instruction with a shifted operand but whatever
everything is already evil
an alternative is to have a special reg-imm format instruction like
ADR RD, RA, IMM16
which is RD = PC + (IMM16 << 16)
RB is left as MBZ
and is implicitly the program counter instead
and its basically just an add with an implicit pc operand
or a lui
or something
idk
i think that's better yeah
well idk why i put RA there just to not use it
RA and RB would both be MBZ i suppose
well reg-imm only has two reg fields, one is the destination and one is mbz
could also make it ra = rb + pc + (imm16 << 16) but idk if that'd actually be useful for anything
it would also require a weird third adder input
ah true i didn't consider it from an actual hw design perspective
im not a digital design person but i know enough to avoid dumb stuff like that at least
in an emulator that wouldn't be a particularly big deal but ig in hardware it's more so
ADR RD, IMM16 RD = PC + (IMM16 << 16) is probably the move
whwere adr is encoded as reg-imm with RA being mbz (or ignored)
least evil way to do it
that seems good yeah
i'd make it mbz instead of ignored so if necessary in the future you can use that field as opcode space
however unlikely that is to be necessary
im scared of shifting the jalr immediate left by two
i think that may mess up the twos complement
of a full twos complement 32 bit addend
nvm it doesnt
i forgot that -4 also has the low 2 bits clear
so you can encode -128 for example as 0xFFFF in the ADR part and then 0x3FFF in the JALR part
er
i said -128 and then started doing the math for -4
silly
but what if you need a 64 bit offset?
f ex within a file
itd be 0xFFFF in the ADR part and then 0x3FE0 in the JALR part, i guess
and this forms (0xFFFF << 16) + (0x3FE0 << 2) = 0xFFFFFF80
which is -128
yeah that sounds good
this does mean i will have to actually implement operand modifiers in GAS instead of relying on bits outside of the operand field to be silently masked
but i should be doing that anyway so i will take it as motivation
i use the RtlUquad structure for this
it is a single UQUAD on 64 bit targets and two ULONGs otherwise
time is an RtlUquad of milliseconds for example
theres a bunch of macros for comparisons and arithmetic with this
which just Do The Thing on 64 bit and synthesize it on 32 bit
alr i've edited binutils so that jalr takes an unsigned immediate instead of signed
i will do adr once the opcode has been decided on
ill decide it rn
that only requires me to find a free slot in a big spreadsheet
it shall be 0b110000
these should be the only binutils changes necessary, i will commit once tested ```patch
diff --git a/patches/binutils/jinx-working-patch.patch b/patches/binutils/jinx-working-patch.patch
index 35dc189..5ca179a 100644
--- a/patches/binutils/jinx-working-patch.patch
+++ b/patches/binutils/jinx-working-patch.patch
@@ -2357,7 +2357,7 @@ diff -urN --no-dereference binutils-clean/opcodes/xr17032-dis.c binutils-workdir
diff -urN --no-dereference binutils-clean/opcodes/xr17032-opc.c binutils-workdir/opcodes/xr17032-opc.c
--- binutils-clean/opcodes/xr17032-opc.c 1970-01-01 01:00:00.000000000 +0100
+++ binutils-workdir/opcodes/xr17032-opc.c
-@@ -0,0 +1,232 @@
+@@ -0,0 +1,234 @@
+/* xr17032-opc.c -- Table of opcodes for the XR/17032 processor.
- Copyright (C) 2025-2025 Free Software Foundation, Inc.
- Contributed by monkuous
@@ -2406,6 +2406,7 @@ diff -urN --no-dereference binutils-clean/opcodes/xr17032-opc.c binutils-workdir
+#define OPC_MOVLLI 0x0000002b
+#define OPC_SLTI 0x0000002c
+#define OPC_BLT 0x0000002d
++#define OPC_ADR 0x00000030
+#define OPC_SYS 0x00000031
+#define OPC_MOVSIIR 0x00000032
+#define OPC_MOVLII 0x00000033
@@ -2468,6 +2469,7 @@ diff -urN --no-dereference binutils-clean/opcodes/xr17032-opc.c binutils-workdir - {"andi", 0, OPC06, OPC_ANDI, {regA, regB, imm16}},
- {"xori", 0, OPC06, OPC_XORI, {regA, regB, imm16}},
- {"ori", 0, OPC06, OPC_ORI, {regA, regB, imm16}},
++ {"adr", 0, OPC06, OPC_ADR, {regA, imm16_16}}, - {"lui", 0, OPC06, OPC_LUI, {regA, regB,
-
imm16_16}}, - {"jalr", 0, OPC06, OPC_JALR, {regA, regB,
https://docs.google.com/spreadsheets/d/1_Jd7liFix44tB3j3iqGc8jPO0XrMacKceCg_UB_r-y0/edit?usp=sharing
btw this exists
worked but i realized it really needs a new relocation because this is the first pcrel 16 bit immediate
i might do something a little weird
adr might be rd = (pc + 4) + (imm16 << 16)
so youd have to account for this by subtracting 4 from the overall offset
the reason is that whenever pc's value is taken on a data path eventually stored into a register, its always pc + 4
so it would be a special case to need just pc here
wait there's precedent for this? i didn't see any instructions that store anything derived from pc into a register
jal and jalr
ohh right
an implementation might do that as feeding pc+4 into the alu with the operation being an add with 0 or something, and then the result goes on the common result path for storage into the register file
sounds reasonable
and then theres a common way to give pc+4 to the alu
which is reused by adr
even the branches probably should have been relative to pc+4 rather than relative to pc
(which is also what dec alpha does)
but that ship has sailed
hm i'm having trouble finding out how bfd wants people to represent this
i see
so akin to a LARGE_INTEGER on nt
represent what
yes
and NT used it for the same reason
non-zero offset from pc when doing pcrel relocations
they probably also didnt have native 64 bit int support in the language
not all C compilers when they started designing it in 1988 let you use 64 bit values directly
check dec alpha
how they do relocations for alpha's branches
youll find the answer there prob
cuz alpha did branches relative to pc+4
well. iirc at least
oh i made a big woopsie
apparently this is common to almost all risc architectures lol
not just alpha
i386 does it as well
oh welllll
actually it might be the responsibility of whoever creates the relocation to apply the offset in the addend
if this were 2022 i would just change that right now and recompile stuff to use the new branch encoding but i feel like i dont have liberty to do that anymore
the horror of having to maintain abi compatibility
this branch thing seems like a pretty egregious silliness
on my part
the reason is obvious too
its because of pipelining
ah i think it's this in the assembler code
by the time you calculate the branch target, the next instruction has been fetched and pc has been incremented
is why
that should be pretty easy considering fixp contains reloc type
doing it my way isnt actually thaaaaat bad, you do have to maintain a shadow pc that the old pc is stored into before being incremented
and you calculate the branch target relative to the shadow pc
but thats still extra logic
for no good reason
oh actually this is making an earlier decision bite me in the ass
i opted to use REL style relocs instead of RELA which leave the addend in the target field
but that doesnt work well when the addend isn't representable in the target field like this
also i think you need something like this anyway because synchronous exceptions exist
you need to know the address of an instruction that can cause an exception that might not materialize until a later pipeline stage
so youre already storing that somewhere
so its maybe not that big of a deal perhaps
i also have a feeling that this entire notion disintegrates when you do OoO anyway and it begins to make almost no difference because OoO maybe necessitates pc<->instruction association bookkeeping that fulfills this role anyway
so this might actually be nearly inconsequential
still embarrassing
@wicked lichen are you embarrassed too
@icy bridge how furious would you be if i actually make this change to the branches
this seems fairly bad actually and i might want to do it as a last second base architecture change
i might slip in other breaking changes too since im already doing one
i wouldn't really mind
i think it'd make my job easier actually if all pcrel relocs behaved the same
its quite strange to me that in like riscv
the branches have the +4 bias
but auipc does not
i was about to do the exact reverse
auipc should have the same bias by my understanding of the reasoning for it being there in the branches
for the same reason
riscv has a lot of nonsensical design decisions
but arm adr also does not do the +4 bias
might just be that whichever architecture is at the root of the "inspiration tree" (don't know the right term) did it, and they did it for the exact same reason you almost got into the opposite situation
this is weird
arm's branches are encoded two instructions ahead
+8 instead of +4
because of historical uarch reasons in like an 80s arm chip or something
im starting to think this doesnt actually matter
and people just smooth it over anyway
iirc thumb2 on arm does something cursed and it's +5, since it does +4 for backwards compat and lsb is the mode
f
i have a hunch that in an OoO design the cleanest thing would just be to always use the unbiased pc
and the +4 might actually introduce "extra" logic when youre doing OoO
im unsure though
my understanding is that in OoO the sequential association between instructions is completely broken down (ensuring dependencies are satisfied, notwithstanding)
so then possibly youd have to store a shadow PC for each of these while theyre inflight and youd need the unbiased PC because thats usually how its reported if an exception were to occur
if you dont use a biased offset in the encoding then you can reuse the unbiased shadow PC you were already storing. otherwise you need to fiddle it a little bit
this could be complete nonsense though im not a uarch engineer
that specific example might be nonsense but it does sound like the reason for the PC+4 bias is usually for uarch reasons that dont necessarily hold on different designs and could even introduce extra logic
so basically what im saying is that it might just not actually matter
because you need to pave over it anyway in the uarch logic
two seconds later
no matter what you pick
i do think it could be valuable to keep it consistent at least
so for that reason
ill leave the pc used by adr unbiased
to align with the branches
and not do this breaking change
sounds good
if anybody knows uarch and knows my reasoning is faulty for some reason and im about to make a huge mistake pls inform
for example aarch64's PC+8 sounds like it was accounting for instruction fetch in a dual issue design in the mid 90s or something
WAIT
okay i made a goof
heres something interesting
ARM had the PC+8 bias
aarch64 has
wait for it
NO BIAS
in the offset encoding!
when i was looking at darwin/iOS stacktraces on arm i found that the addresses were 4 bytes before the relevant jump
does it? rv is by far the cleanest mainstream arch
overall, sure, but that says more about other mainstream arches than it says about riscv
i have been summoned somewhere
hello i have heard that you are working on a compiler targeting xr17032 and i was wondering if you've decided on a call convention for arguments/return values larger than a word yet
fwiw the riscv docs give the rationale why exactly the jump encoding was chosen
because if so i would like to make my gcc port compatible with it
probably the plan is to do a sorta sysv thing where if you can fit an aggregate value into remaining registers, then do it, otherwise pass via pointer
although with aggregate OUT parameters those might just be passed by pointer regardless
i was thinking of doing this
oh so you just translate the returns and OUT params into a c struct
that makes sense
yeah jackal rn treats out parameters as extra return values and passes them through a3, a2, ...
yea yea
i don't want to extend the actual c syntax so i thought making structs pass through those regs would be a better option
what happens if you have a function that returns 5 values
does the last one get passed as a pointer arg
or does the whole thing get passed as a pointer
as a ‘return value’ struct
personally i think i'll pass the whole thing through a pointer
but that's not what jackal does rn
jackal puts the first 4 in regs, and the others in the incoming argument area on the stack
hm
i don't like sacrificing abi compatibility but there's not much i can do without extending the c syntax or making all structs >4 words be split between regs and stack when returned which seems ugly
i just find the idea of treating all the returns as a single struct like
it makes sense for C
yeah but not so much for jackal
but it feels weird to bend around the native calling convention for what C wants and i dont necessarily want to sacrifice jackal performance for c compatibility
realistically its probably not a huge deal
it just feels like a step backwards from jackal by treating everything like a single return value
then again, it probably wont actually affect existing jackal code
i dont know of any functions that return 5 things so its probably fine
i don't like it all that much either but imo making sure all c functions can call all jackal functions and vice versa is important
yeah
there's not really many good solutions since c just lacks the concept of multiple returned values
such is the story of many languages forced through the funnel of the c abi
yeah this is a good strategy
jackal has this because its useful optimization wise to be able to receive multiple returned values without causing a pointer to a local to escape
once a pointer to a local escapes it gets way more difficult for the compiler to figure out if it can keep using the same value across function calls or if it needs to reload it
basically requires whole program analysis
or whatever its called
unless you annotate it somehow
is there any jackal code you know of rn that actually uses the stack for returning values through out? because the only functions that are affected are those with >=5 out params (or 4 if not void)
this change shouldnt affect existing code afaik
=5 looks like a scary angry face
ture
and no probably not
interprocedural alias analysis yeah
scary
also df had multiple return thru registers as well and i needed to be sure the transition to jackal didnt cause accidental performance regressions due to jackal only having C-like multiple return with pointer passing
which is slower
yeah that makes sense
a number of things jackal does are direct analogues to dragonfruit features or syntax
like the function pointer syntax
is one to one dragonfruit
also the preference for unsigned types rather than signed types
is another carry over (and aligns with xr17032's zero extension)
but thats more of a convention than a language thing
alr so this is the pseudocode for what i'm imagining ```
allocateArgument(size) {
num_registers = ceil(size / WORD_SIZE);
if (num_registers <= 2 && current_arg_register + num_registers <= total_arg_registers) {
reg = current_arg_register;
current_arg_register += num_registers;
return REG(reg);
} else {
stack_offset = current_stack_offset;
current_stack_offset += align_up(size, WORD_SIZE);
return STACK(stack_offset);
}
}
/* note: if this returns REG(...), allocate in reverse */
allocateReturnValues(total_size) {
num_registers = ceil(total_size / WORD_SIZE);
if (num_registers <= total_arg_registers) {
return REG(total_arg_registers - num_registers);
} else {
return POINTER_IN_FIRST_ARG;
}
}
- return values treated as a single struct (first return value is first field of struct, each field is aligned to word size)
- caller allocates stack space for arguments and potentially return value if there's no better location (up to the compiler's discretion)
- nothing is split between regs and stack
- each argument is rounded up to word size before allocation
- arguments that are allocated on the stack have increasing addresses
- arguments can take at most two registers, return value does not have this limit
- if return value doesn't fit in registers, a pointer to the output location is passed as the first argument (this must not overlap stack-allocated incoming args), and this pointer is returned in a3
- if return value is passed through registers, the registers are allocated backwards (a3 contains first word)
- varargs are always passed on the stack
i'm still not sure about the last point since passing a pointer to put it in would improve stack usage with large return values (this is common in e.g. rust)
but putting it on the stack at offset 0 would better match existing jackal behavior
the first point might be confusing for interop code
yeah it just seemed like the easiest way to do the whole reverse register allocation thing jackal does
hm im not sure it would make a difference
hm
does the reverse register allocation for args thing actually make a difference btw? i thought it would be so that commonly used arguments dont get overwritten by calls but they get clobbered anyways
at least the jackal compiler treats them like they do
idk
i would guess not
the reason i'm maintaining it is abi compatibility, not actual codegen impact
@twilit smelt whats the reasoning behind the reversed return value registers again
to minimize regalloc hinting collisions with the arguments being passed in
