Commit 2cbdd55d authored by Keith Randall's avatar Keith Randall

[dev.ssa] cmd/compile: fix PIC for SSA-generated code

Access to globals requires a 2-instruction sequence on PIC 386.

    MOVL foo(SB), AX

is translated by the obj package into:

    CALL getPCofNextInstructionInTempRegister(SB)
    MOVL (&foo-&thisInstruction)(tmpReg), AX

The call returns the PC of the next instruction in a register.
The next instruction then offsets from that register to get the
address required.  The tricky part is the allocation of the
temp register.  The legacy compiler always used CX, and forbid
the register allocator from allocating CX when in PIC mode.
We can't easily do that in SSA because CX is actually a required
register for shift instructions. (I think the old backend got away
with this because the register allocator never uses CX, only
codegen knows that shifts must use CX.)

Instead, we allow the temp register to be anything.  When the
destination of the MOV (or LEA) is an integer register, we can
use that register.  Otherwise, we make sure to compile the
operation using an LEA to reference the global.  So

    MOVL AX, foo(SB)

is never generated directly.  Instead, SSA generates:

    LEAL foo(SB), DX
    MOVL AX, (DX)

which is then rewritten by the obj package to:

    CALL getPcInDX(SB)
    LEAL (&foo-&thisInstruction)(DX), AX
    MOVL AX, (DX)

So this CL modifies the obj package to use different thunks
to materialize the pc into different registers.  We use the
registers that regalloc chose so that SSA can still allocate
the full set of registers.

Change-Id: Ie095644f7164a026c62e95baf9d18a8bcaed0bba
Reviewed-on: https://go-review.googlesource.com/25442
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarDavid Chase <drchase@google.com>
parent 69a755b6
...@@ -669,15 +669,21 @@ ...@@ -669,15 +669,21 @@
// We need to fold LEAQ into the MOVx ops so that the live variable analysis knows // We need to fold LEAQ into the MOVx ops so that the live variable analysis knows
// what variables are being read/written by the ops. // what variables are being read/written by the ops.
// Note: we turn off this merging for operations on globals when building position-independent code.
// PIC needs a spare register to load the PC into. For loads from globals into integer registers we use
// the target register, but for other loads and all stores, we need a free register. Having the LEAL be
// a separate instruction gives us that register.
(MOVLload [off1] {sym1} (LEAL [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) -> (MOVLload [off1] {sym1} (LEAL [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOVLload [off1+off2] {mergeSym(sym1,sym2)} base mem) (MOVLload [off1+off2] {mergeSym(sym1,sym2)} base mem)
(MOVWload [off1] {sym1} (LEAL [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) -> (MOVWload [off1] {sym1} (LEAL [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOVWload [off1+off2] {mergeSym(sym1,sym2)} base mem) (MOVWload [off1+off2] {mergeSym(sym1,sym2)} base mem)
(MOVBload [off1] {sym1} (LEAL [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) -> (MOVBload [off1] {sym1} (LEAL [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOVBload [off1+off2] {mergeSym(sym1,sym2)} base mem) (MOVBload [off1+off2] {mergeSym(sym1,sym2)} base mem)
(MOVSSload [off1] {sym1} (LEAL [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) -> (MOVSSload [off1] {sym1} (LEAL [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2)
&& (base.Op != OpSB || !b.Func.Config.ctxt.Flag_shared) ->
(MOVSSload [off1+off2] {mergeSym(sym1,sym2)} base mem) (MOVSSload [off1+off2] {mergeSym(sym1,sym2)} base mem)
(MOVSDload [off1] {sym1} (LEAL [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) -> (MOVSDload [off1] {sym1} (LEAL [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2)
&& (base.Op != OpSB || !b.Func.Config.ctxt.Flag_shared) ->
(MOVSDload [off1+off2] {mergeSym(sym1,sym2)} base mem) (MOVSDload [off1+off2] {mergeSym(sym1,sym2)} base mem)
(MOVBLSXload [off1] {sym1} (LEAL [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) -> (MOVBLSXload [off1] {sym1} (LEAL [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
...@@ -685,22 +691,30 @@ ...@@ -685,22 +691,30 @@
(MOVWLSXload [off1] {sym1} (LEAL [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) -> (MOVWLSXload [off1] {sym1} (LEAL [off2] {sym2} base) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOVWLSXload [off1+off2] {mergeSym(sym1,sym2)} base mem) (MOVWLSXload [off1+off2] {mergeSym(sym1,sym2)} base mem)
(MOVLstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) -> (MOVLstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2)
&& (base.Op != OpSB || !b.Func.Config.ctxt.Flag_shared) ->
(MOVLstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) (MOVLstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
(MOVWstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) -> (MOVWstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2)
&& (base.Op != OpSB || !b.Func.Config.ctxt.Flag_shared) ->
(MOVWstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) (MOVWstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
(MOVBstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) -> (MOVBstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2)
&& (base.Op != OpSB || !b.Func.Config.ctxt.Flag_shared) ->
(MOVBstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) (MOVBstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
(MOVSSstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) -> (MOVSSstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2)
&& (base.Op != OpSB || !b.Func.Config.ctxt.Flag_shared) ->
(MOVSSstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) (MOVSSstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
(MOVSDstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) -> (MOVSDstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2)
&& (base.Op != OpSB || !b.Func.Config.ctxt.Flag_shared) ->
(MOVSDstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) (MOVSDstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
(MOVLstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem) && canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off) -> (MOVLstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem) && canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)
&& (ptr.Op != OpSB || !b.Func.Config.ctxt.Flag_shared) ->
(MOVLstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem) (MOVLstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
(MOVWstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem) && canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off) -> (MOVWstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem) && canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)
&& (ptr.Op != OpSB || !b.Func.Config.ctxt.Flag_shared) ->
(MOVWstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem) (MOVWstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
(MOVBstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem) && canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off) -> (MOVBstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem) && canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)
&& (ptr.Op != OpSB || !b.Func.Config.ctxt.Flag_shared) ->
(MOVBstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem) (MOVBstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
// generating indexed loads and stores // generating indexed loads and stores
......
...@@ -488,6 +488,12 @@ func (s *regAllocState) init(f *Func) { ...@@ -488,6 +488,12 @@ func (s *regAllocState) init(f *Func) {
s.allocatable &^= 1 << 9 // R9 s.allocatable &^= 1 << 9 // R9
case "arm64": case "arm64":
// nothing to do? // nothing to do?
case "386":
// nothing to do.
// Note that for Flag_shared (position independent code)
// we do need to be careful, but that carefulness is hidden
// in the rewrite rules so we always have a free register
// available for global load/stores. See gen/386.rules (search for Flag_shared).
default: default:
s.f.Config.fe.Unimplementedf(0, "arch %s not implemented", s.f.Config.arch) s.f.Config.fe.Unimplementedf(0, "arch %s not implemented", s.f.Config.arch)
} }
......
...@@ -2863,7 +2863,7 @@ func rewriteValue386_Op386MOVBstore(v *Value, config *Config) bool { ...@@ -2863,7 +2863,7 @@ func rewriteValue386_Op386MOVBstore(v *Value, config *Config) bool {
return true return true
} }
// match: (MOVBstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) // match: (MOVBstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem)
// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) // cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !b.Func.Config.ctxt.Flag_shared)
// result: (MOVBstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) // result: (MOVBstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
for { for {
off1 := v.AuxInt off1 := v.AuxInt
...@@ -2877,7 +2877,7 @@ func rewriteValue386_Op386MOVBstore(v *Value, config *Config) bool { ...@@ -2877,7 +2877,7 @@ func rewriteValue386_Op386MOVBstore(v *Value, config *Config) bool {
base := v_0.Args[0] base := v_0.Args[0]
val := v.Args[1] val := v.Args[1]
mem := v.Args[2] mem := v.Args[2]
if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) { if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !b.Func.Config.ctxt.Flag_shared)) {
break break
} }
v.reset(Op386MOVBstore) v.reset(Op386MOVBstore)
...@@ -3062,7 +3062,7 @@ func rewriteValue386_Op386MOVBstoreconst(v *Value, config *Config) bool { ...@@ -3062,7 +3062,7 @@ func rewriteValue386_Op386MOVBstoreconst(v *Value, config *Config) bool {
return true return true
} }
// match: (MOVBstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem) // match: (MOVBstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem)
// cond: canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off) // cond: canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off) && (ptr.Op != OpSB || !b.Func.Config.ctxt.Flag_shared)
// result: (MOVBstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem) // result: (MOVBstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
for { for {
sc := v.AuxInt sc := v.AuxInt
...@@ -3075,7 +3075,7 @@ func rewriteValue386_Op386MOVBstoreconst(v *Value, config *Config) bool { ...@@ -3075,7 +3075,7 @@ func rewriteValue386_Op386MOVBstoreconst(v *Value, config *Config) bool {
sym2 := v_0.Aux sym2 := v_0.Aux
ptr := v_0.Args[0] ptr := v_0.Args[0]
mem := v.Args[1] mem := v.Args[1]
if !(canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)) { if !(canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off) && (ptr.Op != OpSB || !b.Func.Config.ctxt.Flag_shared)) {
break break
} }
v.reset(Op386MOVBstoreconst) v.reset(Op386MOVBstoreconst)
...@@ -3725,7 +3725,7 @@ func rewriteValue386_Op386MOVLstore(v *Value, config *Config) bool { ...@@ -3725,7 +3725,7 @@ func rewriteValue386_Op386MOVLstore(v *Value, config *Config) bool {
return true return true
} }
// match: (MOVLstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) // match: (MOVLstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem)
// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) // cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !b.Func.Config.ctxt.Flag_shared)
// result: (MOVLstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) // result: (MOVLstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
for { for {
off1 := v.AuxInt off1 := v.AuxInt
...@@ -3739,7 +3739,7 @@ func rewriteValue386_Op386MOVLstore(v *Value, config *Config) bool { ...@@ -3739,7 +3739,7 @@ func rewriteValue386_Op386MOVLstore(v *Value, config *Config) bool {
base := v_0.Args[0] base := v_0.Args[0]
val := v.Args[1] val := v.Args[1]
mem := v.Args[2] mem := v.Args[2]
if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) { if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !b.Func.Config.ctxt.Flag_shared)) {
break break
} }
v.reset(Op386MOVLstore) v.reset(Op386MOVLstore)
...@@ -3861,7 +3861,7 @@ func rewriteValue386_Op386MOVLstoreconst(v *Value, config *Config) bool { ...@@ -3861,7 +3861,7 @@ func rewriteValue386_Op386MOVLstoreconst(v *Value, config *Config) bool {
return true return true
} }
// match: (MOVLstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem) // match: (MOVLstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem)
// cond: canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off) // cond: canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off) && (ptr.Op != OpSB || !b.Func.Config.ctxt.Flag_shared)
// result: (MOVLstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem) // result: (MOVLstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
for { for {
sc := v.AuxInt sc := v.AuxInt
...@@ -3874,7 +3874,7 @@ func rewriteValue386_Op386MOVLstoreconst(v *Value, config *Config) bool { ...@@ -3874,7 +3874,7 @@ func rewriteValue386_Op386MOVLstoreconst(v *Value, config *Config) bool {
sym2 := v_0.Aux sym2 := v_0.Aux
ptr := v_0.Args[0] ptr := v_0.Args[0]
mem := v.Args[1] mem := v.Args[1]
if !(canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)) { if !(canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off) && (ptr.Op != OpSB || !b.Func.Config.ctxt.Flag_shared)) {
break break
} }
v.reset(Op386MOVLstoreconst) v.reset(Op386MOVLstoreconst)
...@@ -4240,7 +4240,7 @@ func rewriteValue386_Op386MOVSDload(v *Value, config *Config) bool { ...@@ -4240,7 +4240,7 @@ func rewriteValue386_Op386MOVSDload(v *Value, config *Config) bool {
return true return true
} }
// match: (MOVSDload [off1] {sym1} (LEAL [off2] {sym2} base) mem) // match: (MOVSDload [off1] {sym1} (LEAL [off2] {sym2} base) mem)
// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) // cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !b.Func.Config.ctxt.Flag_shared)
// result: (MOVSDload [off1+off2] {mergeSym(sym1,sym2)} base mem) // result: (MOVSDload [off1+off2] {mergeSym(sym1,sym2)} base mem)
for { for {
off1 := v.AuxInt off1 := v.AuxInt
...@@ -4253,7 +4253,7 @@ func rewriteValue386_Op386MOVSDload(v *Value, config *Config) bool { ...@@ -4253,7 +4253,7 @@ func rewriteValue386_Op386MOVSDload(v *Value, config *Config) bool {
sym2 := v_0.Aux sym2 := v_0.Aux
base := v_0.Args[0] base := v_0.Args[0]
mem := v.Args[1] mem := v.Args[1]
if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) { if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !b.Func.Config.ctxt.Flag_shared)) {
break break
} }
v.reset(Op386MOVSDload) v.reset(Op386MOVSDload)
...@@ -4468,7 +4468,7 @@ func rewriteValue386_Op386MOVSDstore(v *Value, config *Config) bool { ...@@ -4468,7 +4468,7 @@ func rewriteValue386_Op386MOVSDstore(v *Value, config *Config) bool {
return true return true
} }
// match: (MOVSDstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) // match: (MOVSDstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem)
// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) // cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !b.Func.Config.ctxt.Flag_shared)
// result: (MOVSDstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) // result: (MOVSDstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
for { for {
off1 := v.AuxInt off1 := v.AuxInt
...@@ -4482,7 +4482,7 @@ func rewriteValue386_Op386MOVSDstore(v *Value, config *Config) bool { ...@@ -4482,7 +4482,7 @@ func rewriteValue386_Op386MOVSDstore(v *Value, config *Config) bool {
base := v_0.Args[0] base := v_0.Args[0]
val := v.Args[1] val := v.Args[1]
mem := v.Args[2] mem := v.Args[2]
if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) { if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !b.Func.Config.ctxt.Flag_shared)) {
break break
} }
v.reset(Op386MOVSDstore) v.reset(Op386MOVSDstore)
...@@ -4710,7 +4710,7 @@ func rewriteValue386_Op386MOVSSload(v *Value, config *Config) bool { ...@@ -4710,7 +4710,7 @@ func rewriteValue386_Op386MOVSSload(v *Value, config *Config) bool {
return true return true
} }
// match: (MOVSSload [off1] {sym1} (LEAL [off2] {sym2} base) mem) // match: (MOVSSload [off1] {sym1} (LEAL [off2] {sym2} base) mem)
// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) // cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !b.Func.Config.ctxt.Flag_shared)
// result: (MOVSSload [off1+off2] {mergeSym(sym1,sym2)} base mem) // result: (MOVSSload [off1+off2] {mergeSym(sym1,sym2)} base mem)
for { for {
off1 := v.AuxInt off1 := v.AuxInt
...@@ -4723,7 +4723,7 @@ func rewriteValue386_Op386MOVSSload(v *Value, config *Config) bool { ...@@ -4723,7 +4723,7 @@ func rewriteValue386_Op386MOVSSload(v *Value, config *Config) bool {
sym2 := v_0.Aux sym2 := v_0.Aux
base := v_0.Args[0] base := v_0.Args[0]
mem := v.Args[1] mem := v.Args[1]
if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) { if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !b.Func.Config.ctxt.Flag_shared)) {
break break
} }
v.reset(Op386MOVSSload) v.reset(Op386MOVSSload)
...@@ -4938,7 +4938,7 @@ func rewriteValue386_Op386MOVSSstore(v *Value, config *Config) bool { ...@@ -4938,7 +4938,7 @@ func rewriteValue386_Op386MOVSSstore(v *Value, config *Config) bool {
return true return true
} }
// match: (MOVSSstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) // match: (MOVSSstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem)
// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) // cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !b.Func.Config.ctxt.Flag_shared)
// result: (MOVSSstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) // result: (MOVSSstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
for { for {
off1 := v.AuxInt off1 := v.AuxInt
...@@ -4952,7 +4952,7 @@ func rewriteValue386_Op386MOVSSstore(v *Value, config *Config) bool { ...@@ -4952,7 +4952,7 @@ func rewriteValue386_Op386MOVSSstore(v *Value, config *Config) bool {
base := v_0.Args[0] base := v_0.Args[0]
val := v.Args[1] val := v.Args[1]
mem := v.Args[2] mem := v.Args[2]
if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) { if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !b.Func.Config.ctxt.Flag_shared)) {
break break
} }
v.reset(Op386MOVSSstore) v.reset(Op386MOVSSstore)
...@@ -5696,7 +5696,7 @@ func rewriteValue386_Op386MOVWstore(v *Value, config *Config) bool { ...@@ -5696,7 +5696,7 @@ func rewriteValue386_Op386MOVWstore(v *Value, config *Config) bool {
return true return true
} }
// match: (MOVWstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) // match: (MOVWstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem)
// cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) // cond: is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !b.Func.Config.ctxt.Flag_shared)
// result: (MOVWstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) // result: (MOVWstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
for { for {
off1 := v.AuxInt off1 := v.AuxInt
...@@ -5710,7 +5710,7 @@ func rewriteValue386_Op386MOVWstore(v *Value, config *Config) bool { ...@@ -5710,7 +5710,7 @@ func rewriteValue386_Op386MOVWstore(v *Value, config *Config) bool {
base := v_0.Args[0] base := v_0.Args[0]
val := v.Args[1] val := v.Args[1]
mem := v.Args[2] mem := v.Args[2]
if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2)) { if !(is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !b.Func.Config.ctxt.Flag_shared)) {
break break
} }
v.reset(Op386MOVWstore) v.reset(Op386MOVWstore)
...@@ -5923,7 +5923,7 @@ func rewriteValue386_Op386MOVWstoreconst(v *Value, config *Config) bool { ...@@ -5923,7 +5923,7 @@ func rewriteValue386_Op386MOVWstoreconst(v *Value, config *Config) bool {
return true return true
} }
// match: (MOVWstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem) // match: (MOVWstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem)
// cond: canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off) // cond: canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off) && (ptr.Op != OpSB || !b.Func.Config.ctxt.Flag_shared)
// result: (MOVWstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem) // result: (MOVWstoreconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
for { for {
sc := v.AuxInt sc := v.AuxInt
...@@ -5936,7 +5936,7 @@ func rewriteValue386_Op386MOVWstoreconst(v *Value, config *Config) bool { ...@@ -5936,7 +5936,7 @@ func rewriteValue386_Op386MOVWstoreconst(v *Value, config *Config) bool {
sym2 := v_0.Aux sym2 := v_0.Aux
ptr := v_0.Args[0] ptr := v_0.Args[0]
mem := v.Args[1] mem := v.Args[1]
if !(canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off)) { if !(canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd(off) && (ptr.Op != OpSB || !b.Func.Config.ctxt.Flag_shared)) {
break break
} }
v.reset(Op386MOVWstoreconst) v.reset(Op386MOVWstoreconst)
......
...@@ -653,15 +653,15 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { ...@@ -653,15 +653,15 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
// See the comments in cmd/internal/obj/x86/obj6.go // See the comments in cmd/internal/obj/x86/obj6.go
// near CanUse1InsnTLS for a detailed explanation of these instructions. // near CanUse1InsnTLS for a detailed explanation of these instructions.
if x86.CanUse1InsnTLS(gc.Ctxt) { if x86.CanUse1InsnTLS(gc.Ctxt) {
// MOVQ (TLS), r // MOVL (TLS), r
p := gc.Prog(x86.AMOVL) p := gc.Prog(x86.AMOVL)
p.From.Type = obj.TYPE_MEM p.From.Type = obj.TYPE_MEM
p.From.Reg = x86.REG_TLS p.From.Reg = x86.REG_TLS
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = r p.To.Reg = r
} else { } else {
// MOVQ TLS, r // MOVL TLS, r
// MOVQ (r)(TLS*1), r // MOVL (r)(TLS*1), r
p := gc.Prog(x86.AMOVL) p := gc.Prog(x86.AMOVL)
p.From.Type = obj.TYPE_REG p.From.Type = obj.TYPE_REG
p.From.Reg = x86.REG_TLS p.From.Reg = x86.REG_TLS
......
...@@ -2835,7 +2835,9 @@ func asmandsz(ctxt *obj.Link, p *obj.Prog, a *obj.Addr, r int, rex int, m64 int) ...@@ -2835,7 +2835,9 @@ func asmandsz(ctxt *obj.Link, p *obj.Prog, a *obj.Addr, r int, rex int, m64 int)
goto bad goto bad
} }
if p.Mode == 32 && ctxt.Flag_shared { if p.Mode == 32 && ctxt.Flag_shared {
base = REG_CX // The base register has already been set. It holds the PC
// of this instruction returned by a PC-reading thunk.
// See obj6.go:rewriteToPcrel.
} else { } else {
base = REG_NONE base = REG_NONE
} }
...@@ -2880,7 +2882,9 @@ func asmandsz(ctxt *obj.Link, p *obj.Prog, a *obj.Addr, r int, rex int, m64 int) ...@@ -2880,7 +2882,9 @@ func asmandsz(ctxt *obj.Link, p *obj.Prog, a *obj.Addr, r int, rex int, m64 int)
ctxt.Diag("bad addr: %v", p) ctxt.Diag("bad addr: %v", p)
} }
if p.Mode == 32 && ctxt.Flag_shared { if p.Mode == 32 && ctxt.Flag_shared {
base = REG_CX // The base register has already been set. It holds the PC
// of this instruction returned by a PC-reading thunk.
// See obj6.go:rewriteToPcrel.
} else { } else {
base = REG_NONE base = REG_NONE
} }
...@@ -4016,25 +4020,26 @@ func doasm(ctxt *obj.Link, p *obj.Prog) { ...@@ -4016,25 +4020,26 @@ func doasm(ctxt *obj.Link, p *obj.Prog) {
obj.Hnacl: obj.Hnacl:
if ctxt.Flag_shared { if ctxt.Flag_shared {
// Note that this is not generating the same insns as the other cases. // Note that this is not generating the same insns as the other cases.
// MOV TLS, R_to // MOV TLS, dst
// becomes // becomes
// call __x86.get_pc_thunk.cx // call __x86.get_pc_thunk.dst
// movl (gotpc + g@gotntpoff)(%ecx),$R_To // movl (gotpc + g@gotntpoff)(dst), dst
// which is encoded as // which is encoded as
// call __x86.get_pc_thunk.cx // call __x86.get_pc_thunk.dst
// movq 0(%ecx), R_to // movq 0(dst), dst
// and R_CALL & R_TLS_IE relocs. This all assumes the only tls variable we access // and R_CALL & R_TLS_IE relocs. This all assumes the only tls variable we access
// is g, which we can't check here, but will when we assemble the second // is g, which we can't check here, but will when we assemble the second
// instruction. // instruction.
dst := p.To.Reg
ctxt.AsmBuf.Put1(0xe8) ctxt.AsmBuf.Put1(0xe8)
r = obj.Addrel(ctxt.Cursym) r = obj.Addrel(ctxt.Cursym)
r.Off = int32(p.Pc + int64(ctxt.AsmBuf.Len())) r.Off = int32(p.Pc + int64(ctxt.AsmBuf.Len()))
r.Type = obj.R_CALL r.Type = obj.R_CALL
r.Siz = 4 r.Siz = 4
r.Sym = obj.Linklookup(ctxt, "__x86.get_pc_thunk.cx", 0) r.Sym = obj.Linklookup(ctxt, "__x86.get_pc_thunk."+strings.ToLower(Rconv(int(dst))), 0)
ctxt.AsmBuf.PutInt32(0) ctxt.AsmBuf.PutInt32(0)
ctxt.AsmBuf.Put2(0x8B, byte(2<<6|reg[REG_CX]|(reg[p.To.Reg]<<3))) ctxt.AsmBuf.Put2(0x8B, byte(2<<6|reg[dst]|(reg[dst]<<3)))
r = obj.Addrel(ctxt.Cursym) r = obj.Addrel(ctxt.Cursym)
r.Off = int32(p.Pc + int64(ctxt.AsmBuf.Len())) r.Off = int32(p.Pc + int64(ctxt.AsmBuf.Len()))
r.Type = obj.R_TLS_IE r.Type = obj.R_TLS_IE
......
...@@ -36,6 +36,7 @@ import ( ...@@ -36,6 +36,7 @@ import (
"fmt" "fmt"
"log" "log"
"math" "math"
"strings"
) )
func CanUse1InsnTLS(ctxt *obj.Link) bool { func CanUse1InsnTLS(ctxt *obj.Link) bool {
...@@ -509,7 +510,7 @@ func rewriteToPcrel(ctxt *obj.Link, p *obj.Prog) { ...@@ -509,7 +510,7 @@ func rewriteToPcrel(ctxt *obj.Link, p *obj.Prog) {
return return
} }
// Any Prog (aside from the above special cases) with an Addr with Name == // Any Prog (aside from the above special cases) with an Addr with Name ==
// NAME_EXTERN, NAME_STATIC or NAME_GOTREF has a CALL __x86.get_pc_thunk.cx // NAME_EXTERN, NAME_STATIC or NAME_GOTREF has a CALL __x86.get_pc_thunk.XX
// inserted before it. // inserted before it.
isName := func(a *obj.Addr) bool { isName := func(a *obj.Addr) bool {
if a.Sym == nil || (a.Type != obj.TYPE_MEM && a.Type != obj.TYPE_ADDR) || a.Reg != 0 { if a.Sym == nil || (a.Type != obj.TYPE_MEM && a.Type != obj.TYPE_ADDR) || a.Reg != 0 {
...@@ -542,12 +543,23 @@ func rewriteToPcrel(ctxt *obj.Link, p *obj.Prog) { ...@@ -542,12 +543,23 @@ func rewriteToPcrel(ctxt *obj.Link, p *obj.Prog) {
if !isName(&p.From) && !isName(&p.To) && (p.From3 == nil || !isName(p.From3)) { if !isName(&p.From) && !isName(&p.To) && (p.From3 == nil || !isName(p.From3)) {
return return
} }
var dst int16 = REG_CX
if isName(&p.From) && p.To.Type == obj.TYPE_REG {
switch p.As {
case ALEAL, AMOVL, AMOVWLZX, AMOVBLZX, AMOVWLSX, AMOVBLSX:
dst = p.To.Reg
// Special case: clobber the destination register with
// the PC so we don't have to clobber CX.
// The SSA backend depends on CX not being clobbered across these instructions.
// See cmd/compile/internal/ssa/gen/386.rules (search for Flag_shared).
}
}
q := obj.Appendp(ctxt, p) q := obj.Appendp(ctxt, p)
q.RegTo2 = 1 q.RegTo2 = 1
r := obj.Appendp(ctxt, q) r := obj.Appendp(ctxt, q)
r.RegTo2 = 1 r.RegTo2 = 1
q.As = obj.ACALL q.As = obj.ACALL
q.To.Sym = obj.Linklookup(ctxt, "__x86.get_pc_thunk.cx", 0) q.To.Sym = obj.Linklookup(ctxt, "__x86.get_pc_thunk."+strings.ToLower(Rconv(int(dst))), 0)
q.To.Type = obj.TYPE_MEM q.To.Type = obj.TYPE_MEM
q.To.Name = obj.NAME_EXTERN q.To.Name = obj.NAME_EXTERN
q.To.Sym.Local = true q.To.Sym.Local = true
...@@ -557,6 +569,15 @@ func rewriteToPcrel(ctxt *obj.Link, p *obj.Prog) { ...@@ -557,6 +569,15 @@ func rewriteToPcrel(ctxt *obj.Link, p *obj.Prog) {
r.From3 = p.From3 r.From3 = p.From3
r.Reg = p.Reg r.Reg = p.Reg
r.To = p.To r.To = p.To
if isName(&p.From) {
r.From.Reg = dst
}
if isName(&p.To) {
r.To.Reg = dst
}
if p.From3 != nil && isName(p.From3) {
r.From3.Reg = dst
}
obj.Nopout(p) obj.Nopout(p)
} }
......
...@@ -55,21 +55,37 @@ func gentext() { ...@@ -55,21 +55,37 @@ func gentext() {
return return
} }
thunkfunc := ld.Linklookup(ld.Ctxt, "__x86.get_pc_thunk.cx", 0) // Generate little thunks that load the PC of the next instruction into a register.
thunkfunc.Type = obj.STEXT for _, r := range [...]struct {
thunkfunc.Attr |= ld.AttrLocal name string
thunkfunc.Attr |= ld.AttrReachable num uint8
o := func(op ...uint8) { }{
for _, op1 := range op { {"ax", 0},
ld.Adduint8(ld.Ctxt, thunkfunc, op1) {"cx", 1},
{"dx", 2},
{"bx", 3},
// sp
{"bp", 5},
{"si", 6},
{"di", 7},
} {
thunkfunc := ld.Linklookup(ld.Ctxt, "__x86.get_pc_thunk."+r.name, 0)
thunkfunc.Type = obj.STEXT
thunkfunc.Attr |= ld.AttrLocal
thunkfunc.Attr |= ld.AttrReachable //TODO: remove?
o := func(op ...uint8) {
for _, op1 := range op {
ld.Adduint8(ld.Ctxt, thunkfunc, op1)
}
} }
} // 8b 04 24 mov (%esp),%eax
// 8b 0c 24 mov (%esp),%ecx // Destination register is in bits 3-5 of the middle byte, so add that in.
o(0x8b, 0x0c, 0x24) o(0x8b, 0x04+r.num<<3, 0x24)
// c3 ret // c3 ret
o(0xc3) o(0xc3)
ld.Ctxt.Textp = append(ld.Ctxt.Textp, thunkfunc) ld.Ctxt.Textp = append(ld.Ctxt.Textp, thunkfunc)
}
addmoduledata := ld.Linklookup(ld.Ctxt, "runtime.addmoduledata", 0) addmoduledata := ld.Linklookup(ld.Ctxt, "runtime.addmoduledata", 0)
if addmoduledata.Type == obj.STEXT { if addmoduledata.Type == obj.STEXT {
...@@ -84,7 +100,7 @@ func gentext() { ...@@ -84,7 +100,7 @@ func gentext() {
initfunc.Type = obj.STEXT initfunc.Type = obj.STEXT
initfunc.Attr |= ld.AttrLocal initfunc.Attr |= ld.AttrLocal
initfunc.Attr |= ld.AttrReachable initfunc.Attr |= ld.AttrReachable
o = func(op ...uint8) { o := func(op ...uint8) {
for _, op1 := range op { for _, op1 := range op {
ld.Adduint8(ld.Ctxt, initfunc, op1) ld.Adduint8(ld.Ctxt, initfunc, op1)
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment