get switch statements to work (#21159)

This commit is contained in:
Walter Bright 2025-04-06 09:17:35 -07:00 committed by GitHub
parent 8bce4f5a1a
commit 63ed7154b7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 60 additions and 24 deletions

View file

@ -1600,6 +1600,7 @@ void assignaddrc(code* c)
case FL.code: case FL.code:
case FL.unde: case FL.unde:
case FL.block: case FL.block:
case FL.switch_:
break; break;
default: default:

View file

@ -1406,7 +1406,7 @@ void cdcnvt(ref CGstate cg, ref CodeBuilder cdb,elem* e, ref regm_t pretregs)
case OPu64_d: // ucvtf d31,x0 case OPu64_d: // ucvtf d31,x0
regm_t retregs1 = ALLREGS; regm_t retregs1 = ALLREGS;
codelem(cgstate,cdb,e.E1,retregs1,false); codelem(cgstate,cdb,e.E1,retregs1,false);
reg_t Rn = findreg(retregs1); // source integer register reg_t Rn = findreg(retregs1); // source integer register
regm_t retregs = INSTR.FLOATREGS; regm_t retregs = INSTR.FLOATREGS;
const tym = tybasic(e.Ety); const tym = tybasic(e.Ety);

View file

@ -2252,16 +2252,20 @@ void disassemble(uint c) @trusted
case ldr(1,0,3): p1 = "ldrsh"; goto Lldr; case ldr(1,0,3): p1 = "ldrsh"; goto Lldr;
case ldr(2,0,0): p1 = "str"; goto Lldr; case ldr(2,0,0): p1 = "str"; goto Lldr;
case ldr(2,0,1): p1 = "ldr"; goto Lldr; case ldr(2,0,1): p1 = "ldr"; goto Lldr;
case ldr(2,0,2): p1 = "ldrsw"; goto Lldr64; case ldr(2,0,2): p1 = "ldrsw"; goto Lldrsw;
case ldr(3,0,0): p1 = "str"; goto Lldr64; case ldr(3,0,0): p1 = "str"; goto Lldr64;
case ldr(3,0,1): p1 = "ldr"; goto Lldr64; case ldr(3,0,1): p1 = "ldr"; goto Lldr64;
//case ldr(3,0,2): p1 = "prfm"; //case ldr(3,0,2): p1 = "prfm";
Lldrsw:
p2 = regString(true, Rt);
p3 = eaString(0, cast(ubyte)Rn, imm12 * 4);
break;
Lldr64: Lldr64:
is64 = true; is64 = true;
Lldr: Lldr:
p2 = regString(is64, Rt); p2 = regString(is64, Rt);
uint offset = imm12 * (is64 ? 8 : 4); p3 = eaString(0, cast(ubyte)Rn, imm12 * (is64 ? 8 : 4));
p3 = eaString(0, cast(ubyte)Rn, offset);
break; break;
case ldr(0,1,0): p1 = "str"; goto LsimdFp; case ldr(0,1,0): p1 = "str"; goto LsimdFp;

View file

@ -937,7 +937,7 @@ void outblkexitcode(ref CodeBuilder cdb, block* bl, ref int anyspill, const(FL)*
case BC.switch_: case BC.switch_:
{ {
assert(!(bl.Bflags & BFL.epilog)); assert(!(bl.Bflags & BFL.epilog));
doswitch(cdb,bl); // hide messy details doswitch(cgstate,cdb,bl); // hide messy details
break; break;
} }
case BC.jcatch: // D catch clause of try-catch case BC.jcatch: // D catch clause of try-catch
@ -1598,9 +1598,20 @@ struct CaseVal
* Generate comparison of [reg2,reg] with val * Generate comparison of [reg2,reg] with val
*/ */
@trusted @trusted
private void cmpval(ref CodeBuilder cdb, targ_llong val, uint sz, reg_t reg, reg_t reg2, reg_t sreg) private void cmpval(CGstate cg, ref CodeBuilder cdb, targ_llong val, uint sz, reg_t reg, reg_t reg2, reg_t sreg)
{ {
if (I64 && sz == 8) if (cg.AArch64)
{
// TODO AArch64 use CMP https://www.scs.stanford.edu/~zyedidia/arm64/cmp_subs_addsub_imm.html
assert(sreg == NOREG);
regm_t retregs = cg.allregs & ~mask(reg);
sreg = allocreg(cdb,retregs,TYint);
movregconst(cdb,sreg,val,sz == 8 ? 64 : 0);
getregsNoSave(retregs);
assert(reg2 == NOREG);
cdb.gen1(INSTR.cmp_shift(sz == 8, reg, 0, 0, sreg)); // CMP sreg,reg
}
else if (I64 && sz == 8)
{ {
assert(reg2 == NOREG); assert(reg2 == NOREG);
if (val == cast(int)val) // if val is a 64 bit value sign-extended from 32 bits if (val == cast(int)val) // if val is a 64 bit value sign-extended from 32 bits
@ -1630,7 +1641,7 @@ private void cmpval(ref CodeBuilder cdb, targ_llong val, uint sz, reg_t reg, reg
} }
@trusted extern (D) @trusted extern (D)
private void ifthen(ref CodeBuilder cdb, scope CaseVal[] casevals, private void ifthen(CGstate cg, ref CodeBuilder cdb, scope CaseVal[] casevals,
uint sz, reg_t reg, reg_t reg2, reg_t sreg, block* bdefault, bool last) uint sz, reg_t reg, reg_t reg2, reg_t sreg, block* bdefault, bool last)
{ {
const ncases = casevals.length; const ncases = casevals.length;
@ -1640,18 +1651,27 @@ private void ifthen(ref CodeBuilder cdb, scope CaseVal[] casevals,
// Compares for casevals[0..pivot] // Compares for casevals[0..pivot]
CodeBuilder cdb1; cdb1.ctor(); CodeBuilder cdb1; cdb1.ctor();
ifthen(cdb1, casevals[0 .. pivot], sz, reg, reg2, sreg, bdefault, true); ifthen(cg, cdb1, casevals[0 .. pivot], sz, reg, reg2, sreg, bdefault, true);
// Compares for casevals[pivot+1..ncases] // Compares for casevals[pivot+1..ncases]
CodeBuilder cdb2; cdb2.ctor(); CodeBuilder cdb2; cdb2.ctor();
ifthen(cdb2, casevals[pivot + 1 .. $], sz, reg, reg2, sreg, bdefault, last); ifthen(cg, cdb2, casevals[pivot + 1 .. $], sz, reg, reg2, sreg, bdefault, last);
code* c2 = gennop(null); code* c2 = gennop(null);
// Compare for caseval[pivot] // Compare for caseval[pivot]
cmpval(cdb, casevals[pivot].val, sz, reg, reg2, sreg); cmpval(cg, cdb, casevals[pivot].val, sz, reg, reg2, sreg);
genjmp(cdb,JE,FL.block,casevals[pivot].target); // JE target if (cg.AArch64)
// Note uint jump here, as cases were sorted using uint comparisons {
genjmp(cdb,JA,FL.code,cast(block*) c2); // JG c2 genBranch(cdb,COND.eq,FL.block,casevals[pivot].target); // equal
// Note uint jump here, as cases were sorted using uint comparisons
genBranch(cdb,COND.cs,FL.code,cast(block*) c2); // greater than
}
else
{
genjmp(cdb,JE,FL.block,casevals[pivot].target); // JE target
// Note uint jump here, as cases were sorted using uint comparisons
genjmp(cdb,JA,FL.code,cast(block*) c2); // JG c2
}
cdb.append(cdb1); cdb.append(cdb1);
cdb.append(c2); cdb.append(c2);
@ -1662,20 +1682,29 @@ private void ifthen(ref CodeBuilder cdb, scope CaseVal[] casevals,
foreach (size_t n; 0 .. ncases) foreach (size_t n; 0 .. ncases)
{ {
targ_llong val = casevals[n].val; targ_llong val = casevals[n].val;
cmpval(cdb, val, sz, reg, reg2, sreg); cmpval(cg, cdb, val, sz, reg, reg2, sreg);
code* cnext = null; code* cnext = null;
if (reg2 != NOREG) if (reg2 != NOREG)
{ {
assert(!cg.AArch64);
cnext = gennop(null); cnext = gennop(null);
genjmp(cdb,JNE,FL.code,cast(block*) cnext); // JNE cnext genjmp(cdb,JNE,FL.code,cast(block*) cnext); // JNE cnext
cdb.genc2(0x81,modregrm(3,7,reg2),cast(targ_size_t)MSREG(val)); // CMP reg2,MSREG(casevalue) cdb.genc2(0x81,modregrm(3,7,reg2),cast(targ_size_t)MSREG(val)); // CMP reg2,MSREG(casevalue)
} }
genjmp(cdb,JE,FL.block,casevals[n].target); // JE caseaddr if (cg.AArch64)
genBranch(cdb,COND.eq,FL.block,casevals[n].target); // JE caseaddr
else
genjmp(cdb,JE,FL.block,casevals[n].target); // JE caseaddr
cdb.append(cnext); cdb.append(cnext);
} }
if (last) // if default is not next block if (last) // if default is not next block
genjmp(cdb,JMP,FL.block,bdefault); {
if (cg.AArch64)
genBranch(cdb,COND.al,FL.block,bdefault);
else
genjmp(cdb,JMP,FL.block,bdefault);
}
} }
} }
@ -1688,7 +1717,7 @@ private void ifthen(ref CodeBuilder cdb, scope CaseVal[] casevals,
*/ */
@trusted @trusted
void doswitch(ref CodeBuilder cdb, block* b) void doswitch(ref CGstate cg, ref CodeBuilder cdb, block* b)
{ {
// If switch tables are in code segment and we need a CS: override to get at them // If switch tables are in code segment and we need a CS: override to get at them
bool csseg = cast(bool)(config.flags & CFGromable); bool csseg = cast(bool)(config.flags & CFGromable);
@ -1732,7 +1761,7 @@ void doswitch(ref CodeBuilder cdb, block* b)
/* Three kinds of switch strategies - pick one /* Three kinds of switch strategies - pick one
*/ */
const ncases = b.Bswitch.length; const ncases = b.Bswitch.length;
if (ncases <= 3) if (ncases <= 3 || cg.AArch64)
goto Lifthen; goto Lifthen;
else if (I16 && cast(targ_ullong)(vmax - vmin) <= ncases * 2) else if (I16 && cast(targ_ullong)(vmax - vmin) <= ncases * 2)
goto Ljmptab; // >=50% of the table is case values, rest is default goto Ljmptab; // >=50% of the table is case values, rest is default
@ -1748,7 +1777,8 @@ void doswitch(ref CodeBuilder cdb, block* b)
/*************************************************************************/ /*************************************************************************/
{ // generate if-then sequence { // generate if-then sequence
Lifthen: Lifthen:
regm_t retregs = ALLREGS; //printf("ifthen:\n");
regm_t retregs = cg.allregs;
b.bc = BC.ifthen; b.bc = BC.ifthen;
scodelem(cgstate,cdb,e,retregs,0,true); scodelem(cgstate,cdb,e,retregs,0,true);
reg_t reg, reg2; reg_t reg, reg2;
@ -1786,8 +1816,8 @@ void doswitch(ref CodeBuilder cdb, block* b)
casevals[n].target = list_block(bl); casevals[n].target = list_block(bl);
// See if we need a scratch register // See if we need a scratch register
if (sreg == NOREG && I64 && sz == 8 && val != cast(int)val) if (!cg.AArch64 && sreg == NOREG && I64 && sz == 8 && val != cast(int)val)
{ regm_t regm = ALLREGS & ~mask(reg); { regm_t regm = cg.allregs & ~mask(reg);
sreg = allocreg(cdb,regm, TYint); sreg = allocreg(cdb,regm, TYint);
} }
} }
@ -1799,7 +1829,7 @@ void doswitch(ref CodeBuilder cdb, block* b)
//printf("casevals[%lld] = x%x\n", n, casevals[n].val); //printf("casevals[%lld] = x%x\n", n, casevals[n].val);
// Generate binary tree of comparisons // Generate binary tree of comparisons
ifthen(cdb, casevals, sz, reg, reg2, sreg, bdefault, bdefault != b.Bnext); ifthen(cg, cdb, casevals, sz, reg, reg2, sreg, bdefault, bdefault != b.Bnext);
cgstate.stackclean--; cgstate.stackclean--;
return; return;
@ -1809,7 +1839,7 @@ void doswitch(ref CodeBuilder cdb, block* b)
{ {
// Use switch value to index into jump table // Use switch value to index into jump table
Ljmptab: Ljmptab:
//printf("Ljmptab:\n"); //printf("jmptab:\n");
b.bc = BC.jmptab; b.bc = BC.jmptab;
@ -2013,6 +2043,7 @@ else
* Note that it has not been tested with MACHOBJ (OSX). * Note that it has not been tested with MACHOBJ (OSX).
*/ */
Lswitch: Lswitch:
//printf("repne scasw:\n");
regm_t retregs = mAX; // SCASW requires AX regm_t retregs = mAX; // SCASW requires AX
if (dword) if (dword)
retregs |= mDX; retregs |= mDX;