stg_ap_pp_fast doesn't pass the argument in the arity=1 case
The body of stg_ap_pp_fast
looks like
arity = TO_W_(StgFunInfoExtra_arity(%GET_FUN_INFO(R1)));
ASSERT(arity > 0);
if (arity == 1) {
Sp_adj(-2);
W_[Sp+WDS(1)] = R3;
W_[Sp+WDS(0)] = stg_ap_p_info;
R1 = R1 + 1;
jump_SAVE_CCCS(%GET_ENTRY(UNTAG(R1)));
}
if (arity == 2) {
Sp_adj(0);
R1 = R1 + 2;
jump %GET_ENTRY(UNTAG(R1)) [R1,R2,R3];
} else {
Sp_adj(-3);
W_[Sp+WDS(2)] = R3;
W_[Sp+WDS(1)] = R2;
if (arity < 4) {
R1 = R1 + arity;
}
BUILD_PAP(2,2,stg_ap_pp_info,FUN);
}
where
// Jump to target, saving CCCS and restoring it on return
#if defined(PROFILING)
#define jump_SAVE_CCCS(target) \
Sp(-1) = CCCS; \
Sp(-2) = stg_restore_cccs_info; \
Sp_adj(-2); \
jump (target) [R1]
#else
#define jump_SAVE_CCCS(target) jump (target) [R1]
#endif
So in the arity=1 case the jump amounts to
jump (%GET_ENTRY(UNTAG(R1))) [R1]
R1 is the function to apply, but we don't pass its argument, R2!
Now, possibly by design, the calling convention of stg_ap_pp_fast
is such that the first argument to apply is in R2, which is the same register that the function R1 will expect to find its argument in. So if nothing happens to disturb R2 (and possibly this is always the case with the NCG), then everything is fine. However, it's definitely not fine for the LLVM backend, which quite reasonably passes undef
for the R2, R3, and R4 arguments when doing the jump. On arm/Android, LLVM decided to clobber r8
(the physical register for R2) in the body of stg_ap_ppp_fast
(which has the same problems as stg_ap_pp_fast
).
This caused a crash for the following program:
main = print (read "3"::Int)
when built with -debug
.
This appears to have been broken by commit f9265dd3 which changed the argument list for jump_SAVE_CCCS
from [*]
to [R1]
.