genapply program is built with wrong constants when cross compiling
Context
Currently, the wasm backend NCG reserves wasm globals for all Cmm global registers, but most of them (most notably R2..R10) are actually unused, the rest of the arguments are passed on the stack using the same calling convention as unregisterised mode. This is not ideal, we want to make full use of these registers to generate faster and more compact code! This is something that I tried to get right initially in !9204 (closed) but wasted a big chunk of time back then, and I'm now trying to give it another shot.
My first step is applying this patch:
diff --git a/compiler/GHC/Platform/Wasm32.hs b/compiler/GHC/Platform/Wasm32.hs
index e55e26cbf84..99fe82b671e 100644
--- a/compiler/GHC/Platform/Wasm32.hs
+++ b/compiler/GHC/Platform/Wasm32.hs
@@ -4,7 +4,6 @@ module GHC.Platform.Wasm32 where
import GHC.Prelude
--- TODO
-#define MACHREGS_NO_REGS 1
--- #define MACHREGS_wasm32 1
+#define MACHREGS_NO_REGS 0
+#define MACHREGS_wasm32 1
#include "CodeGen.Platform.h"
diff --git a/rts/include/stg/MachRegsForHost.h b/rts/include/stg/MachRegsForHost.h
index f401122e5ff..7c045c0214b 100644
--- a/rts/include/stg/MachRegsForHost.h
+++ b/rts/include/stg/MachRegsForHost.h
@@ -75,8 +75,7 @@
#endif
#if defined(wasm32_HOST_ARCH)
-#undef MACHREGS_NO_REGS
-#define MACHREGS_NO_REGS 1
+#define MACHREGS_wasm32 1
#endif
#if defined(loongarch64_HOST_ARCH)
This should turn off the "no regs" mode for wasm32 target. Despite we don't define any register mappings in rts/include/stg/MachRegs/wasm32.h
yet, it should be fine because the rest of CPP logic would automatically set MAX_REAL_VANILLA_REG
, etc to 0
, so nothing is supposed to break at this point. However, once the patch is applied, even a basic hello world would trigger a runtime panic.
Bug
This time, I figured out the panic was due to incorrectly generated AutoApply.cmm
when cross compiling.
_build/stage1/rts/build/cmm/AutoApply.cmm
is generated by _build/stageBoot/bin/wasm32-wasi-genapply
, whose main logic is in _build/stageBoot/utils/genapply/build/Main.o
, and I could get the preprocessed Main.hs
content by adding -fforce-recomp -keep-tmp-files -v
to the command line stolen from hadrian when building Main.o
. Diffing the two preprocessed Main.hs
files revealed the bug: after the patch above has been applied, Main.hs
changed like this:
diff --git a/tmp/tmp.wPbXX9TFxf/ghc1625383_0/ghc_1.hscpp b/tmp/tmp.6HpxzkKX57/ghc1623737_0/ghc_1.hscpp
index f2d172c0c45..db71d256194 100644
--- a/tmp/tmp.wPbXX9TFxf/ghc1625383_0/ghc_1.hscpp
+++ b/tmp/tmp.6HpxzkKX57/ghc1623737_0/ghc_1.hscpp
@@ -83,9 +83,9 @@ type Reg = String
availableRegs :: RegStatus -> ([Reg],[Reg],[Reg],[Reg])
availableRegs Unregisterised = ([],[],[],[])
availableRegs Registerised =
- ( vanillaRegs 0,
- floatRegs 0,
- doubleRegs 0,
+ ( vanillaRegs 6,
+ floatRegs 6,
+ doubleRegs 6,
longRegs 0
)
So MAX_REAL_VANILLA_REG
, MAX_REAL_FLOAT_REG
and MAX_REAL_DOUBLE_REG
are incorrectly set to 6, but the correct value is supposed to be 0. This is no coincidence: the wasm32 cross target is built on a x86_64 host, and x86_64 host defines these literals to 6. The genapply
program is incorrectly depending on host CPP macro, but it's supposed to be generating AutoApply.cmm
for the target!
I have not yet figured out why this bug surfaces only after turning off the "no regs" mode, but it looks like to me this is not wasm32-specific. genapply
must not include host CPP headers to get the information, this is incredibly cursed and it's now clear it's broken for cross compilation in general.
Solution
The target-specific constants are already calculated by deriveConstants
. It already emits DerivedConstants.h
, but the payload is not easily parsed outside the ghc
package. We can make deriveConstants
generate another small file that contains all constants needed by genapply
, make genapply
read this file at runtime, establish this dependency in hadrian, and throw all CPP in genapply
into a fire.