Sample code:
(program entry point)
(accumulate $N)
*($N is one of [1 2 3])
(into 6)
Six!
IR:
Intermediate code for (program entry point): 0 -1
R0: (group leader) (1 incoming) clause 0
JUMP R1 - -
R1: (part of group R0) (1 incoming) clause 0
ALLOCATE 1 0 -
COLLECT_BEGIN 1 - - -
PUSH_CHOICE 0 R2 -
MAKE_VAR A0 - -
ASSIGN V0/$N A0 -
COLLECT_BEGIN 0 - - -
COLLECT_PUSH 0 1 - -
COLLECT_PUSH 0 2 - -
COLLECT_PUSH 0 3 - -
COLLECT_END_R 0 A1 - -
SET_CONT R3 - -
INVOKE_MULTI ($ is one of $)
R2: (group leader) (1 incoming) clause 0
POP_CHOICE 0 - -
COLLECT_END_V 1 6 - -
PRINT_WORDS 1 "Six!" - -
DEALLOCATE 1 - - -
PROCEED 0 - - -
R3: (group leader) (1 incoming) clause 0
COLLECT_PUSH 1 V0/$N - -
JUMP <FAIL> - -
It looks like the instruction that’s crashing is COLLECT_END_V 1 6. COLLECT_END means the end of a (collect $) block, V means the result is a value (instead of a reference), 1 means it’s an (accumulate $) instead of a (collect $), and 6 is the value we’re storing into.
Here’s the code for compiling COLLECT_END_V:
case I_COLLECT_END_V:
if(ci->subop) {
ai = add_instr(AA_AUX_POP_LIST);
ai->oper[0] = (aaoper_t) {AAO_STORE_REG, REG_TMP};
ai = add_instr(AA_MAKE_PAIR_D);
ai->oper[0] = encode_dest(ci->oper[0], prg, 1);
ai->oper[1] = (aaoper_t) {AAO_REG, REG_NIL};
ai->oper[2] = (aaoper_t) {AAO_REG, REG_TMP};
} else {
ai = add_instr(AA_AUX_POP_LIST);
if(ci->oper[0].tag == OPER_TEMP
|| ci->oper[0].tag == OPER_VAR
|| ci->oper[0].tag == OPER_ARG
|| ci->oper[0].tag == VAL_NIL) {
ai->oper[0] = encode_dest(ci->oper[0], prg, 1);
} else {
ai->oper[0] = (aaoper_t) {AAO_STORE_REG, REG_TMP};
ai = add_instr(AA_ASSIGN);
ai->oper[0] = encode_value(ci->oper[0], prg);
ai->oper[1] = (aaoper_t) {AAO_REG, REG_TMP};
}
}
break;
Notably, the case when !ci->subop—that is, (collect $) rather than (accumulate $)—specifically checks if the destination is a temporary, variable, argument, or constant nil before storing to it. If it’s not, it instead stores to REG_TMP, then assigns from there.
So, let’s do the same for ci->subop (that is, (accumulate $)).
ai = add_instr(AA_AUX_POP_LIST);
ai->oper[0] = (aaoper_t) {AAO_STORE_REG, REG_TMP};
ai = add_instr(AA_MAKE_PAIR_D);
ai->oper[1] = (aaoper_t) {AAO_REG, REG_NIL};
ai->oper[2] = (aaoper_t) {AAO_REG, REG_TMP};
// If we can store directly into the result, then do it
if(ci->oper[0].tag == OPER_TEMP
|| ci->oper[0].tag == OPER_VAR
|| ci->oper[0].tag == OPER_ARG
|| ci->oper[0].tag == VAL_NIL) {
ai->oper[0] = encode_dest(ci->oper[0], prg, 1);
} else {
// Otherwise, store into REG_TMP, then unify
ai->oper[0] = (aaoper_t) {AAO_STORE_REG, REG_TMP};
ai = add_instr(AA_ASSIGN);
ai->oper[0] = encode_value(ci->oper[0], prg);
ai->oper[1] = (aaoper_t) {AAO_REG, REG_TMP};
}
And voilà! It works. Another bug squashed.