}
func Test386EndToEnd(t *testing.T) {
- defer func(old string) { objabi.GO386 = old }(objabi.GO386)
- for _, go386 := range []string{"387", "sse2"} {
- t.Logf("GO386=%v", go386)
- objabi.GO386 = go386
- testEndToEnd(t, "386", "386")
- }
+ testEndToEnd(t, "386", "386")
}
func TestARMEndToEnd(t *testing.T) {
import (
"math"
- "os"
- "runtime"
"testing"
)
-// For GO386=387, make sure fucomi* opcodes are not used
-// for comparison operations.
-// Note that this test will fail only on a Pentium MMX
-// processor (with GOARCH=386 GO386=387), as it just runs
-// some code and looks for an unimplemented instruction fault.
-
//go:noinline
func compare1(a, b float64) bool {
return a < b
}
}
-// For GO386=387, make sure fucomi* opcodes are not used
-// for float->int conversions.
-
//go:noinline
func cvt1(a float64) uint64 {
return uint64(a)
// are not converted to quiet NaN (qNaN) values during compilation.
// See issue #27193 for more information.
- // TODO: this method for detecting 387 won't work if the compiler has been
- // built using GOARCH=386 GO386=387 and either the target is a different
- // architecture or the GO386=387 environment variable is not set when the
- // test is run.
- if runtime.GOARCH == "386" && os.Getenv("GO386") == "387" {
- t.Skip("signaling NaNs are not propagated on 387 (issue #27516)")
- }
-
// signaling NaNs
{
const nan = uint32(0x7f800001) // sNaN
REGSP int
MAXWIDTH int64
- Use387 bool // should 386 backend use 387 FP instructions instead of sse2.
SoftFloat bool
PadFrame func(int64) int64
BoundsCheckFunc [ssa.BoundsKindCount]*obj.LSym
ExtendCheckFunc [ssa.BoundsKindCount]*obj.LSym
- // GO386=387
- ControlWord64trunc,
- ControlWord32 *obj.LSym
-
// Wasm
WasmMove,
WasmZero,
_ = types.NewPtr(types.Errortype) // *error
types.NewPtrCacheEnabled = false
ssaConfig = ssa.NewConfig(thearch.LinkArch.Name, *types_, Ctxt, Debug['N'] == 0)
- if thearch.LinkArch.Name == "386" {
- ssaConfig.Set387(thearch.Use387)
- }
ssaConfig.SoftFloat = thearch.SoftFloat
ssaConfig.Race = flag_race
ssaCaches = make([]ssa.Cache, nBackendWorkers)
ExtendCheckFunc[ssa.BoundsSlice3CU] = sysvar("panicExtendSlice3CU")
}
- // GO386=387 runtime definitions
- ControlWord64trunc = sysvar("controlWord64trunc") // uint16
- ControlWord32 = sysvar("controlWord32") // uint16
-
// Wasm (all asm funcs with special ABIs)
WasmMove = sysvar("wasmMove")
WasmZero = sysvar("wasmZero")
// bstart remembers where each block starts (indexed by block ID)
bstart []*obj.Prog
- // 387 port: maps from SSE registers (REG_X?) to 387 registers (REG_F?)
- SSEto387 map[int16]int16
- // Some architectures require a 64-bit temporary for FP-related register shuffling. Examples include x86-387, PPC, and Sparc V8.
+ // Some architectures require a 64-bit temporary for FP-related register shuffling. Examples include PPC and Sparc V8.
ScratchFpMem *Node
maxarg int64 // largest frame size for arguments to calls made by the function
progToBlock[s.pp.next] = f.Blocks[0]
}
- if thearch.Use387 {
- s.SSEto387 = map[int16]int16{}
- }
-
s.ScratchFpMem = e.scratchFpMem
if Ctxt.Flag_locationlists {
useSSE bool // Use SSE for non-float operations
useAvg bool // Use optimizations that need Avg* operations
useHmul bool // Use optimizations that need Hmul* operations
- use387 bool // GO386=387
SoftFloat bool //
Race bool // race detector enabled
NeedsFpScratch bool // No direct move between GP and FP register sets
return c
}
-func (c *Config) Set387(b bool) {
- c.NeedsFpScratch = b
- c.use387 = b
-}
-
func (c *Config) Ctxt() *obj.Link { return c.ctxt }
(Xor(32|16|8) ...) => (XORL ...)
(Neg(32|16|8) ...) => (NEGL ...)
-(Neg32F x) && !config.use387 => (PXOR x (MOVSSconst <typ.Float32> [float32(math.Copysign(0, -1))]))
-(Neg64F x) && !config.use387 => (PXOR x (MOVSDconst <typ.Float64> [math.Copysign(0, -1)]))
-(Neg32F x) && config.use387 => (FCHS x)
-(Neg64F x) && config.use387 => (FCHS x)
+(Neg32F x) => (PXOR x (MOVSSconst <typ.Float32> [float32(math.Copysign(0, -1))]))
+(Neg64F x) => (PXOR x (MOVSDconst <typ.Float64> [math.Copysign(0, -1)]))
(Com(32|16|8) ...) => (NOTL ...)
// Merge load/store to op
((ADD|AND|OR|XOR|SUB|MUL)L x l:(MOVLload [off] {sym} ptr mem)) && canMergeLoadClobber(v, l, x) && clobber(l) => ((ADD|AND|OR|XOR|SUB|MUL)Lload x [off] {sym} ptr mem)
-((ADD|SUB|MUL|DIV)SD x l:(MOVSDload [off] {sym} ptr mem)) && canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l) => ((ADD|SUB|MUL|DIV)SDload x [off] {sym} ptr mem)
-((ADD|SUB|MUL|DIV)SS x l:(MOVSSload [off] {sym} ptr mem)) && canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l) => ((ADD|SUB|MUL|DIV)SSload x [off] {sym} ptr mem)
+((ADD|SUB|MUL|DIV)SD x l:(MOVSDload [off] {sym} ptr mem)) && canMergeLoadClobber(v, l, x) && clobber(l) => ((ADD|SUB|MUL|DIV)SDload x [off] {sym} ptr mem)
+((ADD|SUB|MUL|DIV)SS x l:(MOVSSload [off] {sym} ptr mem)) && canMergeLoadClobber(v, l, x) && clobber(l) => ((ADD|SUB|MUL|DIV)SSload x [off] {sym} ptr mem)
(MOVLstore {sym} [off] ptr y:((ADD|AND|OR|XOR)Lload x [off] {sym} ptr mem) mem) && y.Uses==1 && clobber(y) => ((ADD|AND|OR|XOR)Lmodify [off] {sym} ptr x mem)
(MOVLstore {sym} [off] ptr y:((ADD|SUB|AND|OR|XOR)L l:(MOVLload [off] {sym} ptr mem) x) mem) && y.Uses==1 && l.Uses==1 && clobber(y, l) =>
((ADD|SUB|AND|OR|XOR)Lmodify [off] {sym} ptr x mem)
"SB",
}
-// Notes on 387 support.
-// - The 387 has a weird stack-register setup for floating-point registers.
-// We use these registers when SSE registers are not available (when GO386=387).
-// - We use the same register names (X0-X7) but they refer to the 387
-// floating-point registers. That way, most of the SSA backend is unchanged.
-// - The instruction generation pass maintains an SSE->387 register mapping.
-// This mapping is updated whenever the FP stack is pushed or popped so that
-// we can always find a given SSE register even when the TOS pointer has changed.
-// - To facilitate the mapping from SSE to 387, we enforce that
-// every basic block starts and ends with an empty floating-point stack.
-
func init() {
// Make map from reg names to reg integers.
if len(regNames386) > 64 {
{name: "FlagGT_UGT"}, // signed > and unsigned <
{name: "FlagGT_ULT"}, // signed > and unsigned >
- // Special op for -x on 387
- {name: "FCHS", argLength: 1, reg: fp11},
-
// Special ops for PIC floating-point constants.
// MOVSXconst1 loads the address of the constant-pool entry into a register.
// MOVSXconst2 loads the constant from that address.
Op386FlagLT_UGT
Op386FlagGT_UGT
Op386FlagGT_ULT
- Op386FCHS
Op386MOVSSconst1
Op386MOVSDconst1
Op386MOVSSconst2
argLen: 0,
reg: regInfo{},
},
- {
- name: "FCHS",
- argLen: 1,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
- },
- outputs: []outputInfo{
- {0, 65280}, // X0 X1 X2 X3 X4 X5 X6 X7
- },
- },
- },
{
name: "MOVSSconst1",
auxType: auxFloat32,
s.f.fe.Fatalf(src.NoXPos, "arch %s not implemented", s.f.Config.arch)
}
}
- if s.f.Config.use387 {
- s.allocatable &^= 1 << 15 // X7 disallowed (one 387 register is used as scratch space during SSE->387 generation in ../x86/387.go)
- }
// Linear scan register allocation can be influenced by the order in which blocks appear.
// Decouple the register allocation order from the generated block order.
if phiRegs[i] != noRegister {
continue
}
- if s.f.Config.use387 && v.Type.IsFloat() {
- continue // 387 can't handle floats in registers between blocks
- }
m := s.compatRegs(v.Type) &^ phiUsed &^ s.used
if m != 0 {
r := pickReg(m)
s.freeUseRecords = u
}
- // Spill any values that can't live across basic block boundaries.
- if s.f.Config.use387 {
- s.freeRegs(s.f.Config.fpRegMask)
- }
-
// If we are approaching a merge point and we are the primary
// predecessor of it, find live values that we use soon after
// the merge point and promote them to registers now.
continue
}
v := s.orig[vid]
- if s.f.Config.use387 && v.Type.IsFloat() {
- continue // 387 can't handle floats in registers between blocks
- }
m := s.compatRegs(v.Type) &^ s.used
if m&^desired.avoid != 0 {
m &^= desired.avoid
func rewriteValue386_Op386ADDSD(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
- b := v.Block
- config := b.Func.Config
// match: (ADDSD x l:(MOVSDload [off] {sym} ptr mem))
- // cond: canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)
+ // cond: canMergeLoadClobber(v, l, x) && clobber(l)
// result: (ADDSDload x [off] {sym} ptr mem)
for {
for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
sym := auxToSym(l.Aux)
mem := l.Args[1]
ptr := l.Args[0]
- if !(canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)) {
+ if !(canMergeLoadClobber(v, l, x) && clobber(l)) {
continue
}
v.reset(Op386ADDSDload)
func rewriteValue386_Op386ADDSS(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
- b := v.Block
- config := b.Func.Config
// match: (ADDSS x l:(MOVSSload [off] {sym} ptr mem))
- // cond: canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)
+ // cond: canMergeLoadClobber(v, l, x) && clobber(l)
// result: (ADDSSload x [off] {sym} ptr mem)
for {
for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
sym := auxToSym(l.Aux)
mem := l.Args[1]
ptr := l.Args[0]
- if !(canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)) {
+ if !(canMergeLoadClobber(v, l, x) && clobber(l)) {
continue
}
v.reset(Op386ADDSSload)
func rewriteValue386_Op386DIVSD(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
- b := v.Block
- config := b.Func.Config
// match: (DIVSD x l:(MOVSDload [off] {sym} ptr mem))
- // cond: canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)
+ // cond: canMergeLoadClobber(v, l, x) && clobber(l)
// result: (DIVSDload x [off] {sym} ptr mem)
for {
x := v_0
sym := auxToSym(l.Aux)
mem := l.Args[1]
ptr := l.Args[0]
- if !(canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)) {
+ if !(canMergeLoadClobber(v, l, x) && clobber(l)) {
break
}
v.reset(Op386DIVSDload)
func rewriteValue386_Op386DIVSS(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
- b := v.Block
- config := b.Func.Config
// match: (DIVSS x l:(MOVSSload [off] {sym} ptr mem))
- // cond: canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)
+ // cond: canMergeLoadClobber(v, l, x) && clobber(l)
// result: (DIVSSload x [off] {sym} ptr mem)
for {
x := v_0
sym := auxToSym(l.Aux)
mem := l.Args[1]
ptr := l.Args[0]
- if !(canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)) {
+ if !(canMergeLoadClobber(v, l, x) && clobber(l)) {
break
}
v.reset(Op386DIVSSload)
func rewriteValue386_Op386MULSD(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
- b := v.Block
- config := b.Func.Config
// match: (MULSD x l:(MOVSDload [off] {sym} ptr mem))
- // cond: canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)
+ // cond: canMergeLoadClobber(v, l, x) && clobber(l)
// result: (MULSDload x [off] {sym} ptr mem)
for {
for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
sym := auxToSym(l.Aux)
mem := l.Args[1]
ptr := l.Args[0]
- if !(canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)) {
+ if !(canMergeLoadClobber(v, l, x) && clobber(l)) {
continue
}
v.reset(Op386MULSDload)
func rewriteValue386_Op386MULSS(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
- b := v.Block
- config := b.Func.Config
// match: (MULSS x l:(MOVSSload [off] {sym} ptr mem))
- // cond: canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)
+ // cond: canMergeLoadClobber(v, l, x) && clobber(l)
// result: (MULSSload x [off] {sym} ptr mem)
for {
for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
sym := auxToSym(l.Aux)
mem := l.Args[1]
ptr := l.Args[0]
- if !(canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)) {
+ if !(canMergeLoadClobber(v, l, x) && clobber(l)) {
continue
}
v.reset(Op386MULSSload)
func rewriteValue386_Op386SUBSD(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
- b := v.Block
- config := b.Func.Config
// match: (SUBSD x l:(MOVSDload [off] {sym} ptr mem))
- // cond: canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)
+ // cond: canMergeLoadClobber(v, l, x) && clobber(l)
// result: (SUBSDload x [off] {sym} ptr mem)
for {
x := v_0
sym := auxToSym(l.Aux)
mem := l.Args[1]
ptr := l.Args[0]
- if !(canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)) {
+ if !(canMergeLoadClobber(v, l, x) && clobber(l)) {
break
}
v.reset(Op386SUBSDload)
func rewriteValue386_Op386SUBSS(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
- b := v.Block
- config := b.Func.Config
// match: (SUBSS x l:(MOVSSload [off] {sym} ptr mem))
- // cond: canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)
+ // cond: canMergeLoadClobber(v, l, x) && clobber(l)
// result: (SUBSSload x [off] {sym} ptr mem)
for {
x := v_0
sym := auxToSym(l.Aux)
mem := l.Args[1]
ptr := l.Args[0]
- if !(canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l)) {
+ if !(canMergeLoadClobber(v, l, x) && clobber(l)) {
break
}
v.reset(Op386SUBSSload)
func rewriteValue386_OpNeg32F(v *Value) bool {
v_0 := v.Args[0]
b := v.Block
- config := b.Func.Config
typ := &b.Func.Config.Types
// match: (Neg32F x)
- // cond: !config.use387
// result: (PXOR x (MOVSSconst <typ.Float32> [float32(math.Copysign(0, -1))]))
for {
x := v_0
- if !(!config.use387) {
- break
- }
v.reset(Op386PXOR)
v0 := b.NewValue0(v.Pos, Op386MOVSSconst, typ.Float32)
v0.AuxInt = float32ToAuxInt(float32(math.Copysign(0, -1)))
v.AddArg2(x, v0)
return true
}
- // match: (Neg32F x)
- // cond: config.use387
- // result: (FCHS x)
- for {
- x := v_0
- if !(config.use387) {
- break
- }
- v.reset(Op386FCHS)
- v.AddArg(x)
- return true
- }
- return false
}
func rewriteValue386_OpNeg64F(v *Value) bool {
v_0 := v.Args[0]
b := v.Block
- config := b.Func.Config
typ := &b.Func.Config.Types
// match: (Neg64F x)
- // cond: !config.use387
// result: (PXOR x (MOVSDconst <typ.Float64> [math.Copysign(0, -1)]))
for {
x := v_0
- if !(!config.use387) {
- break
- }
v.reset(Op386PXOR)
v0 := b.NewValue0(v.Pos, Op386MOVSDconst, typ.Float64)
v0.AuxInt = float64ToAuxInt(math.Copysign(0, -1))
v.AddArg2(x, v0)
return true
}
- // match: (Neg64F x)
- // cond: config.use387
- // result: (FCHS x)
- for {
- x := v_0
- if !(config.use387) {
- break
- }
- v.reset(Op386FCHS)
- v.AddArg(x)
- return true
- }
- return false
}
func rewriteValue386_OpNeq16(v *Value) bool {
v_1 := v.Args[1]
+++ /dev/null
-// Copyright 2016 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package x86
-
-import (
- "cmd/compile/internal/gc"
- "cmd/compile/internal/ssa"
- "cmd/compile/internal/types"
- "cmd/internal/obj"
- "cmd/internal/obj/x86"
- "math"
-)
-
-// Generates code for v using 387 instructions.
-func ssaGenValue387(s *gc.SSAGenState, v *ssa.Value) {
- // The SSA compiler pretends that it has an SSE backend.
- // If we don't have one of those, we need to translate
- // all the SSE ops to equivalent 387 ops. That's what this
- // function does.
-
- switch v.Op {
- case ssa.Op386MOVSSconst, ssa.Op386MOVSDconst:
- iv := uint64(v.AuxInt)
- if iv == 0x0000000000000000 { // +0.0
- s.Prog(x86.AFLDZ)
- } else if iv == 0x3ff0000000000000 { // +1.0
- s.Prog(x86.AFLD1)
- } else if iv == 0x8000000000000000 { // -0.0
- s.Prog(x86.AFLDZ)
- s.Prog(x86.AFCHS)
- } else if iv == 0xbff0000000000000 { // -1.0
- s.Prog(x86.AFLD1)
- s.Prog(x86.AFCHS)
- } else if iv == 0x400921fb54442d18 { // +pi
- s.Prog(x86.AFLDPI)
- } else if iv == 0xc00921fb54442d18 { // -pi
- s.Prog(x86.AFLDPI)
- s.Prog(x86.AFCHS)
- } else { // others
- p := s.Prog(loadPush(v.Type))
- p.From.Type = obj.TYPE_FCONST
- p.From.Val = math.Float64frombits(iv)
- p.To.Type = obj.TYPE_REG
- p.To.Reg = x86.REG_F0
- }
- popAndSave(s, v)
-
- case ssa.Op386MOVSSconst2, ssa.Op386MOVSDconst2:
- p := s.Prog(loadPush(v.Type))
- p.From.Type = obj.TYPE_MEM
- p.From.Reg = v.Args[0].Reg()
- p.To.Type = obj.TYPE_REG
- p.To.Reg = x86.REG_F0
- popAndSave(s, v)
-
- case ssa.Op386MOVSSload, ssa.Op386MOVSDload, ssa.Op386MOVSSloadidx1, ssa.Op386MOVSDloadidx1, ssa.Op386MOVSSloadidx4, ssa.Op386MOVSDloadidx8:
- p := s.Prog(loadPush(v.Type))
- p.From.Type = obj.TYPE_MEM
- p.From.Reg = v.Args[0].Reg()
- gc.AddAux(&p.From, v)
- switch v.Op {
- case ssa.Op386MOVSSloadidx1, ssa.Op386MOVSDloadidx1:
- p.From.Scale = 1
- p.From.Index = v.Args[1].Reg()
- if p.From.Index == x86.REG_SP {
- p.From.Reg, p.From.Index = p.From.Index, p.From.Reg
- }
- case ssa.Op386MOVSSloadidx4:
- p.From.Scale = 4
- p.From.Index = v.Args[1].Reg()
- case ssa.Op386MOVSDloadidx8:
- p.From.Scale = 8
- p.From.Index = v.Args[1].Reg()
- }
- p.To.Type = obj.TYPE_REG
- p.To.Reg = x86.REG_F0
- popAndSave(s, v)
-
- case ssa.Op386MOVSSstore, ssa.Op386MOVSDstore:
- // Push to-be-stored value on top of stack.
- push(s, v.Args[1])
-
- // Pop and store value.
- var op obj.As
- switch v.Op {
- case ssa.Op386MOVSSstore:
- op = x86.AFMOVFP
- case ssa.Op386MOVSDstore:
- op = x86.AFMOVDP
- }
- p := s.Prog(op)
- p.From.Type = obj.TYPE_REG
- p.From.Reg = x86.REG_F0
- p.To.Type = obj.TYPE_MEM
- p.To.Reg = v.Args[0].Reg()
- gc.AddAux(&p.To, v)
-
- case ssa.Op386MOVSSstoreidx1, ssa.Op386MOVSDstoreidx1, ssa.Op386MOVSSstoreidx4, ssa.Op386MOVSDstoreidx8:
- push(s, v.Args[2])
- var op obj.As
- switch v.Op {
- case ssa.Op386MOVSSstoreidx1, ssa.Op386MOVSSstoreidx4:
- op = x86.AFMOVFP
- case ssa.Op386MOVSDstoreidx1, ssa.Op386MOVSDstoreidx8:
- op = x86.AFMOVDP
- }
- p := s.Prog(op)
- p.From.Type = obj.TYPE_REG
- p.From.Reg = x86.REG_F0
- p.To.Type = obj.TYPE_MEM
- p.To.Reg = v.Args[0].Reg()
- gc.AddAux(&p.To, v)
- switch v.Op {
- case ssa.Op386MOVSSstoreidx1, ssa.Op386MOVSDstoreidx1:
- p.To.Scale = 1
- p.To.Index = v.Args[1].Reg()
- if p.To.Index == x86.REG_SP {
- p.To.Reg, p.To.Index = p.To.Index, p.To.Reg
- }
- case ssa.Op386MOVSSstoreidx4:
- p.To.Scale = 4
- p.To.Index = v.Args[1].Reg()
- case ssa.Op386MOVSDstoreidx8:
- p.To.Scale = 8
- p.To.Index = v.Args[1].Reg()
- }
-
- case ssa.Op386ADDSS, ssa.Op386ADDSD, ssa.Op386SUBSS, ssa.Op386SUBSD,
- ssa.Op386MULSS, ssa.Op386MULSD, ssa.Op386DIVSS, ssa.Op386DIVSD:
- if v.Reg() != v.Args[0].Reg() {
- v.Fatalf("input[0] and output not in same register %s", v.LongString())
- }
-
- // Push arg1 on top of stack
- push(s, v.Args[1])
-
- // Set precision if needed. 64 bits is the default.
- switch v.Op {
- case ssa.Op386ADDSS, ssa.Op386SUBSS, ssa.Op386MULSS, ssa.Op386DIVSS:
- // Save AX so we can use it as scratch space.
- p := s.Prog(x86.AMOVL)
- p.From.Type = obj.TYPE_REG
- p.From.Reg = x86.REG_AX
- s.AddrScratch(&p.To)
- // Install a 32-bit version of the control word.
- installControlWord(s, gc.ControlWord32, x86.REG_AX)
- // Restore AX.
- p = s.Prog(x86.AMOVL)
- s.AddrScratch(&p.From)
- p.To.Type = obj.TYPE_REG
- p.To.Reg = x86.REG_AX
- }
-
- var op obj.As
- switch v.Op {
- case ssa.Op386ADDSS, ssa.Op386ADDSD:
- op = x86.AFADDDP
- case ssa.Op386SUBSS, ssa.Op386SUBSD:
- op = x86.AFSUBDP
- case ssa.Op386MULSS, ssa.Op386MULSD:
- op = x86.AFMULDP
- case ssa.Op386DIVSS, ssa.Op386DIVSD:
- op = x86.AFDIVDP
- }
- p := s.Prog(op)
- p.From.Type = obj.TYPE_REG
- p.From.Reg = x86.REG_F0
- p.To.Type = obj.TYPE_REG
- p.To.Reg = s.SSEto387[v.Reg()] + 1
-
- // Restore precision if needed.
- switch v.Op {
- case ssa.Op386ADDSS, ssa.Op386SUBSS, ssa.Op386MULSS, ssa.Op386DIVSS:
- restoreControlWord(s)
- }
-
- case ssa.Op386UCOMISS, ssa.Op386UCOMISD:
- push(s, v.Args[0])
-
- // Compare.
- p := s.Prog(x86.AFUCOMP)
- p.From.Type = obj.TYPE_REG
- p.From.Reg = x86.REG_F0
- p.To.Type = obj.TYPE_REG
- p.To.Reg = s.SSEto387[v.Args[1].Reg()] + 1
-
- // Save AX.
- p = s.Prog(x86.AMOVL)
- p.From.Type = obj.TYPE_REG
- p.From.Reg = x86.REG_AX
- s.AddrScratch(&p.To)
-
- // Move status word into AX.
- p = s.Prog(x86.AFSTSW)
- p.To.Type = obj.TYPE_REG
- p.To.Reg = x86.REG_AX
-
- // Then move the flags we need to the integer flags.
- s.Prog(x86.ASAHF)
-
- // Restore AX.
- p = s.Prog(x86.AMOVL)
- s.AddrScratch(&p.From)
- p.To.Type = obj.TYPE_REG
- p.To.Reg = x86.REG_AX
-
- case ssa.Op386SQRTSD:
- push(s, v.Args[0])
- s.Prog(x86.AFSQRT)
- popAndSave(s, v)
-
- case ssa.Op386FCHS:
- push(s, v.Args[0])
- s.Prog(x86.AFCHS)
- popAndSave(s, v)
-
- case ssa.Op386CVTSL2SS, ssa.Op386CVTSL2SD:
- p := s.Prog(x86.AMOVL)
- p.From.Type = obj.TYPE_REG
- p.From.Reg = v.Args[0].Reg()
- s.AddrScratch(&p.To)
- p = s.Prog(x86.AFMOVL)
- s.AddrScratch(&p.From)
- p.To.Type = obj.TYPE_REG
- p.To.Reg = x86.REG_F0
- popAndSave(s, v)
-
- case ssa.Op386CVTTSD2SL, ssa.Op386CVTTSS2SL:
- push(s, v.Args[0])
-
- // Load control word which truncates (rounds towards zero).
- installControlWord(s, gc.ControlWord64trunc, v.Reg())
-
- // Now do the conversion.
- p := s.Prog(x86.AFMOVLP)
- p.From.Type = obj.TYPE_REG
- p.From.Reg = x86.REG_F0
- s.AddrScratch(&p.To)
- p = s.Prog(x86.AMOVL)
- s.AddrScratch(&p.From)
- p.To.Type = obj.TYPE_REG
- p.To.Reg = v.Reg()
-
- // Restore control word.
- restoreControlWord(s)
-
- case ssa.Op386CVTSS2SD:
- // float32 -> float64 is a nop
- push(s, v.Args[0])
- popAndSave(s, v)
-
- case ssa.Op386CVTSD2SS:
- // Round to nearest float32.
- push(s, v.Args[0])
- p := s.Prog(x86.AFMOVFP)
- p.From.Type = obj.TYPE_REG
- p.From.Reg = x86.REG_F0
- s.AddrScratch(&p.To)
- p = s.Prog(x86.AFMOVF)
- s.AddrScratch(&p.From)
- p.To.Type = obj.TYPE_REG
- p.To.Reg = x86.REG_F0
- popAndSave(s, v)
-
- case ssa.OpLoadReg:
- if !v.Type.IsFloat() {
- ssaGenValue(s, v)
- return
- }
- // Load+push the value we need.
- p := s.Prog(loadPush(v.Type))
- gc.AddrAuto(&p.From, v.Args[0])
- p.To.Type = obj.TYPE_REG
- p.To.Reg = x86.REG_F0
- // Move the value to its assigned register.
- popAndSave(s, v)
-
- case ssa.OpStoreReg:
- if !v.Type.IsFloat() {
- ssaGenValue(s, v)
- return
- }
- push(s, v.Args[0])
- var op obj.As
- switch v.Type.Size() {
- case 4:
- op = x86.AFMOVFP
- case 8:
- op = x86.AFMOVDP
- }
- p := s.Prog(op)
- p.From.Type = obj.TYPE_REG
- p.From.Reg = x86.REG_F0
- gc.AddrAuto(&p.To, v)
-
- case ssa.OpCopy:
- if !v.Type.IsFloat() {
- ssaGenValue(s, v)
- return
- }
- push(s, v.Args[0])
- popAndSave(s, v)
-
- case ssa.Op386CALLstatic, ssa.Op386CALLclosure, ssa.Op386CALLinter:
- flush387(s) // Calls must empty the FP stack.
- fallthrough // then issue the call as normal
- default:
- ssaGenValue(s, v)
- }
-}
-
-// push pushes v onto the floating-point stack. v must be in a register.
-func push(s *gc.SSAGenState, v *ssa.Value) {
- p := s.Prog(x86.AFMOVD)
- p.From.Type = obj.TYPE_REG
- p.From.Reg = s.SSEto387[v.Reg()]
- p.To.Type = obj.TYPE_REG
- p.To.Reg = x86.REG_F0
-}
-
-// popAndSave pops a value off of the floating-point stack and stores
-// it in the register assigned to v.
-func popAndSave(s *gc.SSAGenState, v *ssa.Value) {
- r := v.Reg()
- if _, ok := s.SSEto387[r]; ok {
- // Pop value, write to correct register.
- p := s.Prog(x86.AFMOVDP)
- p.From.Type = obj.TYPE_REG
- p.From.Reg = x86.REG_F0
- p.To.Type = obj.TYPE_REG
- p.To.Reg = s.SSEto387[v.Reg()] + 1
- } else {
- // Don't actually pop value. This 387 register is now the
- // new home for the not-yet-assigned-a-home SSE register.
- // Increase the register mapping of all other registers by one.
- for rSSE, r387 := range s.SSEto387 {
- s.SSEto387[rSSE] = r387 + 1
- }
- s.SSEto387[r] = x86.REG_F0
- }
-}
-
-// loadPush returns the opcode for load+push of the given type.
-func loadPush(t *types.Type) obj.As {
- if t.Size() == 4 {
- return x86.AFMOVF
- }
- return x86.AFMOVD
-}
-
-// flush387 removes all entries from the 387 floating-point stack.
-func flush387(s *gc.SSAGenState) {
- for k := range s.SSEto387 {
- p := s.Prog(x86.AFMOVDP)
- p.From.Type = obj.TYPE_REG
- p.From.Reg = x86.REG_F0
- p.To.Type = obj.TYPE_REG
- p.To.Reg = x86.REG_F0
- delete(s.SSEto387, k)
- }
-}
-
-func ssaGenBlock387(s *gc.SSAGenState, b, next *ssa.Block) {
- // Empty the 387's FP stack before the block ends.
- flush387(s)
-
- ssaGenBlock(s, b, next)
-}
-
-// installControlWord saves the current floating-point control
-// word and installs a new one loaded from cw.
-// scratchReg must be an unused register.
-// This call must be paired with restoreControlWord.
-// Bytes 4-5 of the scratch space (s.AddrScratch) are used between
-// this call and restoreControlWord.
-func installControlWord(s *gc.SSAGenState, cw *obj.LSym, scratchReg int16) {
- // Save current control word.
- p := s.Prog(x86.AFSTCW)
- s.AddrScratch(&p.To)
- p.To.Offset += 4
-
- // Materialize address of new control word.
- // Note: this must be a seperate instruction to handle PIE correctly.
- // See issue 41503.
- p = s.Prog(x86.ALEAL)
- p.From.Type = obj.TYPE_MEM
- p.From.Name = obj.NAME_EXTERN
- p.From.Sym = cw
- p.To.Type = obj.TYPE_REG
- p.To.Reg = scratchReg
-
- // Load replacement control word.
- p = s.Prog(x86.AFLDCW)
- p.From.Type = obj.TYPE_MEM
- p.From.Reg = scratchReg
-}
-func restoreControlWord(s *gc.SSAGenState) {
- p := s.Prog(x86.AFLDCW)
- s.AddrScratch(&p.From)
- p.From.Offset += 4
-}
import (
"cmd/compile/internal/gc"
"cmd/internal/obj/x86"
- "cmd/internal/objabi"
- "fmt"
- "os"
)
func Init(arch *gc.Arch) {
arch.LinkArch = &x86.Link386
arch.REGSP = x86.REGSP
- switch v := objabi.GO386; v {
- case "387":
- arch.Use387 = true
- arch.SSAGenValue = ssaGenValue387
- arch.SSAGenBlock = ssaGenBlock387
- case "sse2":
- arch.SSAGenValue = ssaGenValue
- arch.SSAGenBlock = ssaGenBlock
- default:
- fmt.Fprintf(os.Stderr, "unsupported setting GO386=%s\n", v)
- gc.Exit(1)
- }
+ arch.SSAGenValue = ssaGenValue
+ arch.SSAGenBlock = ssaGenBlock
arch.MAXWIDTH = (1 << 32) - 1
arch.ZeroRange = zerorange
if gc.Debug_checknil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers
gc.Warnl(v.Pos, "generated nil check")
}
- case ssa.Op386FCHS:
- v.Fatalf("FCHS in non-387 mode")
case ssa.OpClobber:
p := s.Prog(x86.AMOVL)
p.From.Type = obj.TYPE_CONST
gohostos string
goos string
goarm string
- go386 string
gomips string
gomips64 string
goppc64 string
}
goarm = b
- b = os.Getenv("GO386")
- if b == "" {
- if cansse2() {
- b = "sse2"
- } else {
- b = "387"
- }
- }
- go386 = b
-
b = os.Getenv("GOMIPS")
if b == "" {
b = "hardfloat"
defaultldso = os.Getenv("GO_LDSO")
// For tools being invoked but also for os.ExpandEnv.
- os.Setenv("GO386", go386)
os.Setenv("GOARCH", goarch)
os.Setenv("GOARM", goarm)
os.Setenv("GOHOSTARCH", gohostarch)
if goarch == "arm" {
xprintf(format, "GOARM", goarm)
}
- if goarch == "386" {
- xprintf(format, "GO386", go386)
- }
if goarch == "mips" || goarch == "mipsle" {
xprintf(format, "GOMIPS", gomips)
}
// package objabi
//
// const defaultGOROOT = <goroot>
-// const defaultGO386 = <go386>
// const defaultGOARM = <goarm>
// const defaultGOMIPS = <gomips>
// const defaultGOMIPS64 = <gomips64>
fmt.Fprintln(&buf)
fmt.Fprintf(&buf, "import \"runtime\"\n")
fmt.Fprintln(&buf)
- fmt.Fprintf(&buf, "const defaultGO386 = `%s`\n", go386)
fmt.Fprintf(&buf, "const defaultGOARM = `%s`\n", goarm)
fmt.Fprintf(&buf, "const defaultGOMIPS = `%s`\n", gomips)
fmt.Fprintf(&buf, "const defaultGOMIPS64 = `%s`\n", gomips64)
+++ /dev/null
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build !gccgo
-
-TEXT ·cpuid(SB),$0-8
- MOVL ax+4(FP), AX
- CPUID
- MOVL info+0(FP), DI
- MOVL AX, 0(DI)
- MOVL BX, 4(DI)
- MOVL CX, 8(DI)
- MOVL DX, 12(DI)
- RET
-
+++ /dev/null
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build !gccgo
-
-TEXT ·cpuid(SB),$0-12
- MOVL ax+8(FP), AX
- CPUID
- MOVQ info+0(FP), DI
- MOVL AX, 0(DI)
- MOVL BX, 4(DI)
- MOVL CX, 8(DI)
- MOVL DX, 12(DI)
- RET
-
+++ /dev/null
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build !386,!amd64,!gccgo
-
-#include "textflag.h"
-
-TEXT ·cpuid(SB),NOSPLIT,$0-0
- RET
package main
-func cpuid(info *[4]uint32, ax uint32)
-
-func cansse2() bool {
- if gohostarch != "386" && gohostarch != "amd64" {
- return false
- }
-
- var info [4]uint32
- cpuid(&info, 1)
- return info[3]&(1<<26) != 0 // SSE2
-}
-
// useVFPv1 tries to execute one VFPv1 instruction on ARM.
// It will crash the current process if VFPv1 is missing.
func useVFPv1()
package main
-/*
-int supports_sse2() {
-#if defined(__i386__) || defined(__x86_64__)
- return __builtin_cpu_supports("sse2");
-#else
- return 0;
-#endif
-}
-*/
-import "C"
-
-func cansse2() bool { return C.supports_sse2() != 0 }
-
func useVFPv1() {}
func useVFPv3() {}
// GOARM
// For GOARCH=arm, the ARM architecture for which to compile.
// Valid values are 5, 6, 7.
-// GO386
-// For GOARCH=386, the floating point instruction set.
-// Valid values are 387, sse2.
// GOMIPS
// For GOARCH=mips{,le}, whether to use floating point instructions.
// Valid values are hardfloat (default), softfloat.
// Used in envcmd.MkEnv and build ID computations.
GOARM = envOr("GOARM", fmt.Sprint(objabi.GOARM))
- GO386 = envOr("GO386", objabi.GO386)
GOMIPS = envOr("GOMIPS", objabi.GOMIPS)
GOMIPS64 = envOr("GOMIPS64", objabi.GOMIPS64)
GOPPC64 = envOr("GOPPC64", fmt.Sprintf("%s%d", "power", objabi.GOPPC64))
switch Goarch {
case "arm":
return "GOARM", GOARM
- case "386":
- return "GO386", GO386
case "mips", "mipsle":
return "GOMIPS", GOMIPS
case "mips64", "mips64le":
}
// sortKeyValues sorts a sequence of lines by key.
-// It differs from sort.Strings in that GO386= sorts after GO=.
+// It differs from sort.Strings in that keys which are GOx where x is an ASCII
+// character smaller than = sort after GO=.
+// (There are no such keys currently. It used to matter for GO386 which was
+// removed in Go 1.16.)
func sortKeyValues(lines []string) {
sort.Slice(lines, func(i, j int) bool {
return lineToKey(lines[i]) < lineToKey(lines[j])
GOARM
For GOARCH=arm, the ARM architecture for which to compile.
Valid values are 5, 6, 7.
- GO386
- For GOARCH=386, the floating point instruction set.
- Valid values are 387, sse2.
GOMIPS
For GOARCH=mips{,le}, whether to use floating point instructions.
Valid values are hardfloat (default), softfloat.
fmt.Fprintf(h, "asm %q %q %q\n", b.toolID("asm"), forcedAsmflags, p.Internal.Asmflags)
}
- // GO386, GOARM, GOMIPS, etc.
+ // GOARM, GOMIPS, etc.
key, val := cfg.GetArchEnv()
fmt.Fprintf(h, "%s=%s\n", key, val)
fmt.Fprintf(h, "linkflags %q\n", p.Internal.Ldflags)
}
- // GO386, GOARM, GOMIPS, etc.
+ // GOARM, GOMIPS, etc.
key, val := cfg.GetArchEnv()
fmt.Fprintf(h, "%s=%s\n", key, val)
GOROOT = envOr("GOROOT", defaultGOROOT)
GOARCH = envOr("GOARCH", defaultGOARCH)
GOOS = envOr("GOOS", defaultGOOS)
- GO386 = envOr("GO386", defaultGO386)
GOAMD64 = goamd64()
GOARM = goarm()
GOMIPS = gomips()
if GOARCH != "amd64" {
Regabi_enabled = 0
}
+
+ if v := os.Getenv("GO386"); v != "" && v != "sse2" {
+ msg := fmt.Sprintf("unsupported setting GO386=%s", v)
+ if v == "387" {
+ msg += ". 387 support was dropped in Go 1.16. Consider using gccgo instead."
+ }
+ log.Fatal(msg)
+ }
}
// Note: must agree with runtime.framepointer_enabled.
FC
GCCGO
GO111MODULE
- GO386
GOARCH
GOARM
GOBIN
func TestConvertNaNs(t *testing.T) {
const snan uint32 = 0x7f800001
-
- // Test to see if a store followed by a load of a signaling NaN
- // maintains the signaling bit. The only platform known to fail
- // this test is 386,GO386=387. The real test below will always fail
- // if the platform can't even store+load a float without mucking
- // with the bits.
- gFloat32 = math.Float32frombits(snan)
- runtime.Gosched() // make sure we don't optimize the store/load away
- r := math.Float32bits(gFloat32)
- if r != snan {
- // This should only happen on 386,GO386=387. We have no way to
- // test for 387, so we just make sure we're at least on 386.
- if runtime.GOARCH != "386" {
- t.Errorf("store/load of sNaN not faithful")
- }
- t.Skip("skipping test, float store+load not faithful")
- }
-
type myFloat32 float32
x := V(myFloat32(math.Float32frombits(snan)))
y := x.Convert(TypeOf(float32(0)))
func gen386() {
p("PUSHFL")
- // Save general purpose registers.
+ // Assign stack offsets.
var l = layout{sp: "SP"}
for _, reg := range regNames386 {
- if reg == "SP" || strings.HasPrefix(reg, "X") {
+ if reg == "SP" {
continue
}
- l.add("MOVL", reg, 4)
- }
-
- // Save the 387 state.
- l.addSpecial(
- "FSAVE %d(SP)\nFLDCW runtime·controlWord64(SB)",
- "FRSTOR %d(SP)",
- 108)
-
- // Save SSE state only if supported.
- lSSE := layout{stack: l.stack, sp: "SP"}
- for i := 0; i < 8; i++ {
- lSSE.add("MOVUPS", fmt.Sprintf("X%d", i), 16)
+ if strings.HasPrefix(reg, "X") {
+ l.add("MOVUPS", reg, 16)
+ } else {
+ l.add("MOVL", reg, 4)
+ }
}
- p("ADJSP $%d", lSSE.stack)
+ p("ADJSP $%d", l.stack)
p("NOP SP")
l.save()
- p("CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1\nJNE nosse")
- lSSE.save()
- label("nosse:")
p("CALL ·asyncPreempt2(SB)")
- p("CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1\nJNE nosse2")
- lSSE.restore()
- label("nosse2:")
l.restore()
- p("ADJSP $%d", -lSSE.stack)
-
+ p("ADJSP $%d", -l.stack)
p("POPFL")
p("RET")
}
TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0
PUSHFL
- ADJSP $264
+ ADJSP $156
NOP SP
MOVL AX, 0(SP)
MOVL CX, 4(SP)
MOVL BP, 16(SP)
MOVL SI, 20(SP)
MOVL DI, 24(SP)
- FSAVE 28(SP)
- FLDCW runtime·controlWord64(SB)
- CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1
- JNE nosse
- MOVUPS X0, 136(SP)
- MOVUPS X1, 152(SP)
- MOVUPS X2, 168(SP)
- MOVUPS X3, 184(SP)
- MOVUPS X4, 200(SP)
- MOVUPS X5, 216(SP)
- MOVUPS X6, 232(SP)
- MOVUPS X7, 248(SP)
-nosse:
+ MOVUPS X0, 28(SP)
+ MOVUPS X1, 44(SP)
+ MOVUPS X2, 60(SP)
+ MOVUPS X3, 76(SP)
+ MOVUPS X4, 92(SP)
+ MOVUPS X5, 108(SP)
+ MOVUPS X6, 124(SP)
+ MOVUPS X7, 140(SP)
CALL ·asyncPreempt2(SB)
- CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1
- JNE nosse2
- MOVUPS 248(SP), X7
- MOVUPS 232(SP), X6
- MOVUPS 216(SP), X5
- MOVUPS 200(SP), X4
- MOVUPS 184(SP), X3
- MOVUPS 168(SP), X2
- MOVUPS 152(SP), X1
- MOVUPS 136(SP), X0
-nosse2:
- FRSTOR 28(SP)
+ MOVUPS 140(SP), X7
+ MOVUPS 124(SP), X6
+ MOVUPS 108(SP), X5
+ MOVUPS 92(SP), X4
+ MOVUPS 76(SP), X3
+ MOVUPS 60(SP), X2
+ MOVUPS 44(SP), X1
+ MOVUPS 28(SP), X0
MOVL 24(SP), DI
MOVL 20(SP), SI
MOVL 16(SP), BP
MOVL 8(SP), DX
MOVL 4(SP), CX
MOVL 0(SP), AX
- ADJSP $-264
+ ADJSP $-156
POPFL
RET
return q, n
}
-// Floating point control word values for GOARCH=386 GO386=387.
+// Floating point control word values.
// Bits 0-5 are bits to disable floating-point exceptions.
// Bits 8-9 are the precision control:
// 0 = single precision a.k.a. float32
// 3 = round toward zero
var (
controlWord64 uint16 = 0x3f + 2<<8 + 0<<10
- controlWord32 = 0x3f + 0<<8 + 0<<10
- controlWord64trunc = 0x3f + 2<<8 + 3<<10
+ controlWord64trunc uint16 = 0x3f + 2<<8 + 3<<10
)
func MulMemSrc(a []uint32, b []float32) {
// 386:`IMULL\s4\([A-Z]+\),\s[A-Z]+`
a[0] *= a[1]
- // 386/sse2:`MULSS\s4\([A-Z]+\),\sX[0-9]+`
+ // 386:`MULSS\s4\([A-Z]+\),\sX[0-9]+`
// amd64:`MULSS\s4\([A-Z]+\),\sX[0-9]+`
b[0] *= b[1]
}
// -------------- //
func DivMemSrc(a []float64) {
- // 386/sse2:`DIVSD\s8\([A-Z]+\),\sX[0-9]+`
+ // 386:`DIVSD\s8\([A-Z]+\),\sX[0-9]+`
// amd64:`DIVSD\s8\([A-Z]+\),\sX[0-9]+`
a[0] /= a[1]
}
func FloatDivs(a []float32) float32 {
// amd64:`DIVSS\s8\([A-Z]+\),\sX[0-9]+`
- // 386/sse2:`DIVSS\s8\([A-Z]+\),\sX[0-9]+`
+ // 386:`DIVSS\s8\([A-Z]+\),\sX[0-9]+`
return a[1] / a[2]
}
package codegen
-import "math"
-
// This file contains codegen tests related to arithmetic
// simplifications and optimizations on float types.
// For codegen tests on integer types, see arithmetic.go.
// --------------------- //
func Mul2(f float64) float64 {
- // 386/sse2:"ADDSD",-"MULSD"
- // 386/387:"FADDDP",-"FMULDP"
+ // 386:"ADDSD",-"MULSD"
// amd64:"ADDSD",-"MULSD"
// arm/7:"ADDD",-"MULD"
// arm64:"FADDD",-"FMULD"
}
func DivPow2(f1, f2, f3 float64) (float64, float64, float64) {
- // 386/sse2:"MULSD",-"DIVSD"
- // 386/387:"FMULDP",-"FDIVDP"
+ // 386:"MULSD",-"DIVSD"
// amd64:"MULSD",-"DIVSD"
// arm/7:"MULD",-"DIVD"
// arm64:"FMULD",-"FDIVD"
// ppc64le:"FMUL",-"FDIV"
x := f1 / 16.0
- // 386/sse2:"MULSD",-"DIVSD"
- // 386/387:"FMULDP",-"FDIVDP"
+ // 386:"MULSD",-"DIVSD"
// amd64:"MULSD",-"DIVSD"
// arm/7:"MULD",-"DIVD"
// arm64:"FMULD",-"FDIVD"
// ppc64le:"FMUL",-"FDIVD"
y := f2 / 0.125
- // 386/sse2:"ADDSD",-"DIVSD",-"MULSD"
- // 386/387:"FADDDP",-"FDIVDP",-"FMULDP"
+ // 386:"ADDSD",-"DIVSD",-"MULSD"
// amd64:"ADDSD",-"DIVSD",-"MULSD"
// arm/7:"ADDD",-"MULD",-"DIVD"
// arm64:"FADDD",-"FMULD",-"FDIVD"
return x, y, z
}
-func getPi() float64 {
- // 386/387:"FLDPI"
- return math.Pi
-}
-
func indexLoad(b0 []float32, b1 float32, idx int) float32 {
// arm64:`FMOVS\s\(R[0-9]+\)\(R[0-9]+\),\sF[0-9]+`
return b0[idx] * b1
func sqrt(x float64) float64 {
// amd64:"SQRTSD"
- // 386/387:"FSQRT" 386/sse2:"SQRTSD"
+ // 386:"SQRTSD"
// arm64:"FSQRTD"
// arm/7:"SQRTD"
// mips/hardfloat:"SQRTD" mips/softfloat:-"SQRTD"
func idxFloat32(x, y []float32, i int) {
var t float32
- // amd64: `MOVSS\t4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), X[0-9]+`
- // 386/sse2: `MOVSS\t4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), X[0-9]+`
+ // amd64: `MOVSS\t4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), X[0-9]+`
+ // 386: `MOVSS\t4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), X[0-9]+`
t = x[i+1]
- // amd64: `MOVSS\tX[0-9]+, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\)`
- // 386/sse2: `MOVSS\tX[0-9]+, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\)`
+ // amd64: `MOVSS\tX[0-9]+, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\)`
+ // 386: `MOVSS\tX[0-9]+, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\)`
y[i+1] = t
- // amd64: `MOVSS\t4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\), X[0-9]+`
- // 386/sse2: `MOVSS\t4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\), X[0-9]+`
+ // amd64: `MOVSS\t4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\), X[0-9]+`
+ // 386: `MOVSS\t4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\), X[0-9]+`
t = x[16*i+1]
- // amd64: `MOVSS\tX[0-9]+, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\)`
- // 386/sse2: `MOVSS\tX[0-9]+, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\)`
+ // amd64: `MOVSS\tX[0-9]+, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\)`
+ // 386: `MOVSS\tX[0-9]+, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[14]\)`
y[16*i+1] = t
}
func idxFloat64(x, y []float64, i int) {
var t float64
- // amd64: `MOVSD\t8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), X[0-9]+`
- // 386/sse2: `MOVSD\t8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), X[0-9]+`
+ // amd64: `MOVSD\t8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), X[0-9]+`
+ // 386: `MOVSD\t8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), X[0-9]+`
t = x[i+1]
- // amd64: `MOVSD\tX[0-9]+, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\)`
- // 386/sse2: `MOVSD\tX[0-9]+, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\)`
+ // amd64: `MOVSD\tX[0-9]+, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\)`
+ // 386: `MOVSD\tX[0-9]+, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\)`
y[i+1] = t
- // amd64: `MOVSD\t8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\), X[0-9]+`
- // 386/sse2: `MOVSD\t8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\), X[0-9]+`
+ // amd64: `MOVSD\t8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\), X[0-9]+`
+ // 386: `MOVSD\t8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\), X[0-9]+`
t = x[16*i+1]
- // amd64: `MOVSD\tX[0-9]+, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\)`
- // 386/sse2: `MOVSD\tX[0-9]+, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\)`
+ // amd64: `MOVSD\tX[0-9]+, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\)`
+ // 386: `MOVSD\tX[0-9]+, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*[18]\)`
y[16*i+1] = t
}
// value[0] is the variant-changing environment variable, and values[1:]
// are the supported variants.
archVariants = map[string][]string{
- "386": {"GO386", "387", "sse2"},
+ "386": {},
"amd64": {},
"arm": {"GOARM", "5", "6", "7"},
"arm64": {},
found bool // true if the opcode check matched at least one in the output
}
-// A build environment triplet separated by slashes (eg: linux/386/sse2).
+// A build environment triplet separated by slashes (eg: linux/arm/7).
// The third field can be empty if the arch does not support variants (eg: "plan9/amd64/")
type buildEnv string
// Environ returns the environment it represents in cmd.Environ() "key=val" format
-// For instance, "linux/386/sse2".Environ() returns {"GOOS=linux", "GOARCH=386", "GO386=sse2"}
+// For instance, "linux/arm/7".Environ() returns {"GOOS=linux", "GOARCH=arm", "GOARM=7"}
func (b buildEnv) Environ() []string {
fields := strings.Split(string(b), "/")
if len(fields) != 3 {
var arch, subarch, os string
switch {
- case archspec[2] != "": // 3 components: "linux/386/sse2"
+ case archspec[2] != "": // 3 components: "linux/arm/7"
os, arch, subarch = archspec[0], archspec[1][1:], archspec[2][1:]
- case archspec[1] != "": // 2 components: "386/sse2"
+ case archspec[1] != "": // 2 components: "arm/7"
os, arch, subarch = "linux", archspec[0], archspec[1][1:]
- default: // 1 component: "386"
+ default: // 1 component: "arm"
os, arch, subarch = "linux", archspec[0], ""
if arch == "wasm" {
os = "js"