Skip to content

Commit 540f8c2

Browse files
committed
cmd/compile: use jump table on ARM64
Following CL 357330, use jump tables on ARM64. name old time/op new time/op delta Switch8Predictable-4 3.41ns ± 0% 3.21ns ± 0% ~ (p=0.079 n=4+5) Switch8Unpredictable-4 12.0ns ± 0% 9.5ns ± 0% -21.17% (p=0.000 n=5+4) Switch32Predictable-4 3.06ns ± 0% 2.82ns ± 0% -7.78% (p=0.008 n=5+5) Switch32Unpredictable-4 13.3ns ± 0% 9.5ns ± 0% -28.87% (p=0.016 n=4+5) SwitchStringPredictable-4 3.71ns ± 0% 3.21ns ± 0% -13.43% (p=0.000 n=5+4) SwitchStringUnpredictable-4 14.8ns ± 0% 15.1ns ± 0% +2.37% (p=0.008 n=5+5) Change-Id: Ia0b85df7ca9273cf70c05eb957225c6e61822fa6 Reviewed-on: https://go-review.googlesource.com/c/go/+/403979 TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Keith Randall <[email protected]> Run-TryBot: Cherry Mui <[email protected]> Reviewed-by: David Chase <[email protected]>
1 parent ba8310c commit 540f8c2

File tree

8 files changed

+87
-35
lines changed

8 files changed

+87
-35
lines changed

src/cmd/compile/internal/arm64/ssa.go

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -99,21 +99,22 @@ func genshift(s *ssagen.State, v *ssa.Value, as obj.As, r0, r1, r int16, typ int
9999
return p
100100
}
101101

102-
// generate the memory operand for the indexed load/store instructions
103-
func genIndexedOperand(v *ssa.Value) obj.Addr {
102+
// generate the memory operand for the indexed load/store instructions.
103+
// base and idx are registers.
104+
func genIndexedOperand(op ssa.Op, base, idx int16) obj.Addr {
104105
// Reg: base register, Index: (shifted) index register
105-
mop := obj.Addr{Type: obj.TYPE_MEM, Reg: v.Args[0].Reg()}
106-
switch v.Op {
106+
mop := obj.Addr{Type: obj.TYPE_MEM, Reg: base}
107+
switch op {
107108
case ssa.OpARM64MOVDloadidx8, ssa.OpARM64MOVDstoreidx8, ssa.OpARM64MOVDstorezeroidx8,
108109
ssa.OpARM64FMOVDloadidx8, ssa.OpARM64FMOVDstoreidx8:
109-
mop.Index = arm64.REG_LSL | 3<<5 | v.Args[1].Reg()&31
110+
mop.Index = arm64.REG_LSL | 3<<5 | idx&31
110111
case ssa.OpARM64MOVWloadidx4, ssa.OpARM64MOVWUloadidx4, ssa.OpARM64MOVWstoreidx4, ssa.OpARM64MOVWstorezeroidx4,
111112
ssa.OpARM64FMOVSloadidx4, ssa.OpARM64FMOVSstoreidx4:
112-
mop.Index = arm64.REG_LSL | 2<<5 | v.Args[1].Reg()&31
113+
mop.Index = arm64.REG_LSL | 2<<5 | idx&31
113114
case ssa.OpARM64MOVHloadidx2, ssa.OpARM64MOVHUloadidx2, ssa.OpARM64MOVHstoreidx2, ssa.OpARM64MOVHstorezeroidx2:
114-
mop.Index = arm64.REG_LSL | 1<<5 | v.Args[1].Reg()&31
115+
mop.Index = arm64.REG_LSL | 1<<5 | idx&31
115116
default: // not shifted
116-
mop.Index = v.Args[1].Reg()
117+
mop.Index = idx
117118
}
118119
return mop
119120
}
@@ -465,7 +466,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
465466
ssa.OpARM64FMOVDloadidx8,
466467
ssa.OpARM64FMOVSloadidx4:
467468
p := s.Prog(v.Op.Asm())
468-
p.From = genIndexedOperand(v)
469+
p.From = genIndexedOperand(v.Op, v.Args[0].Reg(), v.Args[1].Reg())
469470
p.To.Type = obj.TYPE_REG
470471
p.To.Reg = v.Reg()
471472
case ssa.OpARM64LDAR,
@@ -504,7 +505,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
504505
ssa.OpARM64MOVDstoreidx8,
505506
ssa.OpARM64FMOVDstoreidx8:
506507
p := s.Prog(v.Op.Asm())
507-
p.To = genIndexedOperand(v)
508+
p.To = genIndexedOperand(v.Op, v.Args[0].Reg(), v.Args[1].Reg())
508509
p.From.Type = obj.TYPE_REG
509510
p.From.Reg = v.Args[2].Reg()
510511
case ssa.OpARM64STP:
@@ -533,7 +534,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
533534
ssa.OpARM64MOVWstorezeroidx4,
534535
ssa.OpARM64MOVDstorezeroidx8:
535536
p := s.Prog(v.Op.Asm())
536-
p.To = genIndexedOperand(v)
537+
p.To = genIndexedOperand(v.Op, v.Args[0].Reg(), v.Args[1].Reg())
537538
p.From.Type = obj.TYPE_REG
538539
p.From.Reg = arm64.REGZERO
539540
case ssa.OpARM64MOVQstorezero:
@@ -1325,6 +1326,20 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
13251326
s.CombJump(b, next, &leJumps)
13261327
case ssa.BlockARM64GTnoov:
13271328
s.CombJump(b, next, &gtJumps)
1329+
1330+
case ssa.BlockARM64JUMPTABLE:
1331+
// MOVD (TABLE)(IDX<<3), Rtmp
1332+
// JMP (Rtmp)
1333+
p := s.Prog(arm64.AMOVD)
1334+
p.From = genIndexedOperand(ssa.OpARM64MOVDloadidx8, b.Controls[1].Reg(), b.Controls[0].Reg())
1335+
p.To.Type = obj.TYPE_REG
1336+
p.To.Reg = arm64.REGTMP
1337+
p = s.Prog(obj.AJMP)
1338+
p.To.Type = obj.TYPE_MEM
1339+
p.To.Reg = arm64.REGTMP
1340+
// Save jump tables for later resolution of the target blocks.
1341+
s.JumpTables = append(s.JumpTables, b)
1342+
13281343
default:
13291344
b.Fatalf("branch not implemented: %s", b.LongString())
13301345
}

src/cmd/compile/internal/ssa/gen/ARM64.rules

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,8 @@
534534

535535
(If cond yes no) => (TBNZ [0] cond yes no)
536536

537+
(JumpTable idx) => (JUMPTABLE {makeJumpTableSym(b)} idx (MOVDaddr <typ.Uintptr> {makeJumpTableSym(b)} (SB)))
538+
537539
// atomic intrinsics
538540
// Note: these ops do not accept offset.
539541
(AtomicLoad8 ...) => (LDARB ...)

src/cmd/compile/internal/ssa/gen/ARM64Ops.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -773,6 +773,12 @@ func init() {
773773
{name: "LEnoov", controls: 1}, // 'LE' but without honoring overflow
774774
{name: "GTnoov", controls: 1}, // 'GT' but without honoring overflow
775775
{name: "GEnoov", controls: 1}, // 'GE' but without honoring overflow
776+
777+
// JUMPTABLE implements jump tables.
778+
// Aux is the symbol (an *obj.LSym) for the jump table.
779+
// control[0] is the index into the jump table.
780+
// control[1] is the address of the jump table (the address of the symbol stored in Aux).
781+
{name: "JUMPTABLE", controls: 2, aux: "Sym"},
776782
}
777783

778784
archs = append(archs, arch{

src/cmd/compile/internal/ssa/opGen.go

Lines changed: 26 additions & 24 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cmd/compile/internal/ssa/rewriteARM64.go

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cmd/internal/obj/arm64/asm7.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1222,6 +1222,16 @@ func span7(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
12221222
// so instruction sequences that use REGTMP are unsafe to
12231223
// preempt asynchronously.
12241224
obj.MarkUnsafePoints(c.ctxt, c.cursym.Func().Text, c.newprog, c.isUnsafePoint, c.isRestartable)
1225+
1226+
// Now that we know byte offsets, we can generate jump table entries.
1227+
for _, jt := range cursym.Func().JumpTables {
1228+
for i, p := range jt.Targets {
1229+
// The ith jumptable entry points to the p.Pc'th
1230+
// byte in the function symbol s.
1231+
// TODO: try using relative PCs.
1232+
jt.Sym.WriteAddr(ctxt, int64(i)*8, 8, cursym, p.Pc)
1233+
}
1234+
}
12251235
}
12261236

12271237
// isUnsafePoint returns whether p is an unsafe point.

src/cmd/internal/sys/arch.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ var ArchARM64 = &Arch{
130130
MinLC: 4,
131131
Alignment: 1,
132132
CanMergeLoads: true,
133+
CanJumpTable: true,
133134
HasLR: true,
134135
FixedFrameSize: 8, // LR
135136
}

test/codegen/switch.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ func f(x string) int {
2424
// use jump tables for 8+ int cases
2525
func square(x int) int {
2626
// amd64:`JMP\s\(.*\)\(.*\)$`
27+
// arm64:`MOVD\s\(R.*\)\(R.*<<3\)`,`JMP\s\(R.*\)$`
2728
switch x {
2829
case 1:
2930
return 1
@@ -49,6 +50,7 @@ func square(x int) int {
4950
// use jump tables for 8+ string lengths
5051
func length(x string) int {
5152
// amd64:`JMP\s\(.*\)\(.*\)$`
53+
// arm64:`MOVD\s\(R.*\)\(R.*<<3\)`,`JMP\s\(R.*\)$`
5254
switch x {
5355
case "a":
5456
return 1

0 commit comments

Comments
 (0)