// Copyright 2009 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/internal/obj"
"cmd/internal/obj/x86"
)
/*
* attempt to generate 64-bit
* res = n
* return 1 on success, 0 if op not handled.
*/
func cgen64(n *gc.Node, res *gc.Node) {
if res.Op != gc.OINDREG && res.Op != gc.ONAME {
gc.Dump("n", n)
gc.Dump("res", res)
gc.Fatal("cgen64 %v of %v", gc.Oconv(int(n.Op), 0), gc.Oconv(int(res.Op), 0))
}
switch n.Op {
default:
gc.Fatal("cgen64 %v", gc.Oconv(int(n.Op), 0))
case gc.OMINUS:
gc.Cgen(n.Left, res)
var hi1 gc.Node
var lo1 gc.Node
split64(res, &lo1, &hi1)
gins(x86.ANEGL, nil, &lo1)
gins(x86.AADCL, ncon(0), &hi1)
gins(x86.ANEGL, nil, &hi1)
splitclean()
return
case gc.OCOM:
gc.Cgen(n.Left, res)
var lo1 gc.Node
var hi1 gc.Node
split64(res, &lo1, &hi1)
gins(x86.ANOTL, nil, &lo1)
gins(x86.ANOTL, nil, &hi1)
splitclean()
return
// binary operators.
// common setup below.
case gc.OADD,
gc.OSUB,
gc.OMUL,
gc.OLROT,
gc.OLSH,
gc.ORSH,
gc.OAND,
gc.OOR,
gc.OXOR:
break
}
l := n.Left
r := n.Right
if !l.Addable {
var t1 gc.Node
gc.Tempname(&t1, l.Type)
gc.Cgen(l, &t1)
l = &t1
}
if r != nil && !r.Addable {
var t2 gc.Node
gc.Tempname(&t2, r.Type)
gc.Cgen(r, &t2)
r = &t2
}
var ax gc.Node
gc.Nodreg(&ax, gc.Types[gc.TINT32], x86.REG_AX)
var cx gc.Node
gc.Nodreg(&cx, gc.Types[gc.TINT32], x86.REG_CX)
var dx gc.Node
gc.Nodreg(&dx, gc.Types[gc.TINT32], x86.REG_DX)
// Setup for binary operation.
var hi1 gc.Node
var lo1 gc.Node
split64(l, &lo1, &hi1)
var lo2 gc.Node
var hi2 gc.Node
if gc.Is64(r.Type) {
split64(r, &lo2, &hi2)
}
// Do op. Leave result in DX:AX.
switch n.Op {
// TODO: Constants
case gc.OADD:
gins(x86.AMOVL, &lo1, &ax)
gins(x86.AMOVL, &hi1, &dx)
gins(x86.AADDL, &lo2, &ax)
gins(x86.AADCL, &hi2, &dx)
// TODO: Constants.
case gc.OSUB:
gins(x86.AMOVL, &lo1, &ax)
gins(x86.AMOVL, &hi1, &dx)
gins(x86.ASUBL, &lo2, &ax)
gins(x86.ASBBL, &hi2, &dx)
// let's call the next two EX and FX.
case gc.OMUL:
var ex gc.Node
gc.Regalloc(&ex, gc.Types[gc.TPTR32], nil)
var fx gc.Node
gc.Regalloc(&fx, gc.Types[gc.TPTR32], nil)
// load args into DX:AX and EX:CX.
gins(x86.AMOVL, &lo1, &ax)
gins(x86.AMOVL, &hi1, &dx)
gins(x86.AMOVL, &lo2, &cx)
gins(x86.AMOVL, &hi2, &ex)
// if DX and EX are zero, use 32 x 32 -> 64 unsigned multiply.
gins(x86.AMOVL, &dx, &fx)
gins(x86.AORL, &ex, &fx)
p1 := gc.Gbranch(x86.AJNE, nil, 0)
gins(x86.AMULL, &cx, nil) // implicit &ax
p2 := gc.Gbranch(obj.AJMP, nil, 0)
gc.Patch(p1, gc.Pc)
// full 64x64 -> 64, from 32x32 -> 64.
gins(x86.AIMULL, &cx, &dx)
gins(x86.AMOVL, &ax, &fx)
gins(x86.AIMULL, &ex, &fx)
gins(x86.AADDL, &dx, &fx)
gins(x86.AMOVL, &cx, &dx)
gins(x86.AMULL, &dx, nil) // implicit &ax
gins(x86.AADDL, &fx, &dx)
gc.Patch(p2, gc.Pc)
gc.Regfree(&ex)
gc.Regfree(&fx)
// We only rotate by a constant c in [0,64).
// if c >= 32:
// lo, hi = hi, lo
// c -= 32
// if c == 0:
// no-op
// else:
// t = hi
// shld hi:lo, c
// shld lo:t, c
case gc.OLROT:
v := uint64(r.Int())
if v >= 32 {
// reverse during load to do the first 32 bits of rotate
v -= 32
gins(x86.AMOVL, &lo1, &dx)
gins(x86.AMOVL, &hi1, &ax)
} else {
gins(x86.AMOVL, &lo1, &ax)
gins(x86.AMOVL, &hi1, &dx)
}
if v == 0 {
} else // done
{
gins(x86.AMOVL, &dx, &cx)
p1 := gins(x86.ASHLL, ncon(uint32(v)), &dx)
p1.From.Index = x86.REG_AX // double-width shift
p1.From.Scale = 0
p1 = gins(x86.ASHLL, ncon(uint32(v)), &ax)
p1.From.Index = x86.REG_CX // double-width shift
p1.From.Scale = 0
}
case gc.OLSH:
if r.Op == gc.OLITERAL {
v := uint64(r.Int())
if v >= 64 {
if gc.Is64(r.Type) {
splitclean()
}
splitclean()
split64(res, &lo2, &hi2)
gins(x86.AMOVL, ncon(0), &lo2)
gins(x86.AMOVL, ncon(0), &hi2)
splitclean()
return
}
if v >= 32 {
if gc.Is64(r.Type) {
splitclean()
}
split64(res, &lo2, &hi2)
gmove(&lo1, &hi2)
if v > 32 {
gins(x86.ASHLL, ncon(uint32(v-32)), &hi2)
}
gins(x86.AMOVL, ncon(0), &lo2)
splitclean()
splitclean()
return
}
// general shift
gins(x86.AMOVL, &lo1, &ax)
gins(x86.AMOVL, &hi1, &dx)
p1 := gins(x86.ASHLL, ncon(uint32(v)), &dx)
p1.From.Index = x86.REG_AX // double-width shift
p1.From.Scale = 0
gins(x86.ASHLL, ncon(uint32(v)), &ax)
break
}
// load value into DX:AX.
gins(x86.AMOVL, &lo1, &ax)
gins(x86.AMOVL, &hi1, &dx)
// load shift value into register.
// if high bits are set, zero value.
var p1 *obj.Prog
if gc.Is64(r.Type) {
gins(x86.ACMPL, &hi2, ncon(0))
p1 = gc.Gbranch(x86.AJNE, nil, +1)
gins(x86.AMOVL, &lo2, &cx)
} else {
cx.Type = gc.Types[gc.TUINT32]
gmove(r, &cx)
}
// if shift count is >=64, zero value
gins(x86.ACMPL, &cx, ncon(64))
p2 := gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1)
if p1 != nil {
gc.Patch(p1, gc.Pc)
}
gins(x86.AXORL, &dx, &dx)
gins(x86.AXORL, &ax, &ax)
gc.Patch(p2, gc.Pc)
// if shift count is >= 32, zero low.
gins(x86.ACMPL, &cx, ncon(32))
p1 = gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1)
gins(x86.AMOVL, &ax, &dx)
gins(x86.ASHLL, &cx, &dx) // SHLL only uses bottom 5 bits of count
gins(x86.AXORL, &ax, &ax)
p2 = gc.Gbranch(obj.AJMP, nil, 0)
gc.Patch(p1, gc.Pc)
// general shift
p1 = gins(x86.ASHLL, &cx, &dx)
p1.From.Index = x86.REG_AX // double-width shift
p1.From.Scale = 0
gins(x86.ASHLL, &cx, &ax)
gc.Patch(p2, gc.Pc)
case gc.ORSH:
if r.Op == gc.OLITERAL {
v := uint64(r.Int())
if v >= 64 {
if gc.Is64(r.Type) {
splitclean()
}
splitclean()
split64(res, &lo2, &hi2)
if hi1.Type.Etype == gc.TINT32 {
gmove(&hi1, &lo2)
gins(x86.ASARL, ncon(31), &lo2)
gmove(&hi1, &hi2)
gins(x86.ASARL, ncon(31), &hi2)
} else {
gins(x86.AMOVL, ncon(0), &lo2)
gins(x86.AMOVL, ncon(0), &hi2)
}
splitclean()
return
}
if v >= 32 {
if gc.Is64(r.Type) {
splitclean()
}
split64(res, &lo2, &hi2)
gmove(&hi1, &lo2)
if v > 32 {
gins(optoas(gc.ORSH, hi1.Type), ncon(uint32(v-32)), &lo2)
}
if hi1.Type.Etype == gc.TINT32 {
gmove(&hi1, &hi2)
gins(x86.ASARL, ncon(31), &hi2)
} else {
gins(x86.AMOVL, ncon(0), &hi2)
}
splitclean()
splitclean()
return
}
// general shift
gins(x86.AMOVL, &lo1, &ax)
gins(x86.AMOVL, &hi1, &dx)
p1 := gins(x86.ASHRL, ncon(uint32(v)), &ax)
p1.From.Index = x86.REG_DX // double-width shift
p1.From.Scale = 0
gins(optoas(gc.ORSH, hi1.Type), ncon(uint32(v)), &dx)
break
}
// load value into DX:AX.
gins(x86.AMOVL, &lo1, &ax)
gins(x86.AMOVL, &hi1, &dx)
// load shift value into register.
// if high bits are set, zero value.
var p1 *obj.Prog
if gc.Is64(r.Type) {
gins(x86.ACMPL, &hi2, ncon(0))
p1 = gc.Gbranch(x86.AJNE, nil, +1)
gins(x86.AMOVL, &lo2, &cx)
} else {
cx.Type = gc.Types[gc.TUINT32]
gmove(r, &cx)
}
// if shift count is >=64, zero or sign-extend value
gins(x86.ACMPL, &cx, ncon(64))
p2 := gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1)
if p1 != nil {
gc.Patch(p1, gc.Pc)
}
if hi1.Type.Etype == gc.TINT32 {
gins(x86.ASARL, ncon(31), &dx)
gins(x86.AMOVL, &dx, &ax)
} else {
gins(x86.AXORL, &dx, &dx)
gins(x86.AXORL, &ax, &ax)
}
gc.Patch(p2, gc.Pc)
// if shift count is >= 32, sign-extend hi.
gins(x86.ACMPL, &cx, ncon(32))
p1 = gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1)
gins(x86.AMOVL, &dx, &ax)
if hi1.Type.Etype == gc.TINT32 {
gins(x86.ASARL, &cx, &ax) // SARL only uses bottom 5 bits of count
gins(x86.ASARL, ncon(31), &dx)
} else {
gins(x86.ASHRL, &cx, &ax)
gins(x86.AXORL, &dx, &dx)
}
p2 = gc.Gbranch(obj.AJMP, nil, 0)
gc.Patch(p1, gc.Pc)
// general shift
p1 = gins(x86.ASHRL, &cx, &ax)
p1.From.Index = x86.REG_DX // double-width shift
p1.From.Scale = 0
gins(optoas(gc.ORSH, hi1.Type), &cx, &dx)
gc.Patch(p2, gc.Pc)
// make constant the right side (it usually is anyway).
case gc.OXOR,
gc.OAND,
gc.OOR:
if lo1.Op == gc.OLITERAL {
nswap(&lo1, &lo2)
nswap(&hi1, &hi2)
}
if lo2.Op == gc.OLITERAL {
// special cases for constants.
lv := uint32(lo2.Int())
hv := uint32(hi2.Int())
splitclean() // right side
split64(res, &lo2, &hi2)
switch n.Op {
case gc.OXOR:
gmove(&lo1, &lo2)
gmove(&hi1, &hi2)
switch lv {
case 0:
break
case 0xffffffff:
gins(x86.ANOTL, nil, &lo2)
default:
gins(x86.AXORL, ncon(lv), &lo2)
}
switch hv {
case 0:
break
case 0xffffffff:
gins(x86.ANOTL, nil, &hi2)
default:
gins(x86.AXORL, ncon(hv), &hi2)
}
case gc.OAND:
switch lv {
case 0:
gins(x86.AMOVL, ncon(0), &lo2)
default:
gmove(&lo1, &lo2)
if lv != 0xffffffff {
gins(x86.AANDL, ncon(lv), &lo2)
}
}
switch hv {
case 0:
gins(x86.AMOVL, ncon(0), &hi2)
default:
gmove(&hi1, &hi2)
if hv != 0xffffffff {
gins(x86.AANDL, ncon(hv), &hi2)
}
}
case gc.OOR:
switch lv {
case 0:
gmove(&lo1, &lo2)
case 0xffffffff:
gins(x86.AMOVL, ncon(0xffffffff), &lo2)
default:
gmove(&lo1, &lo2)
gins(x86.AORL, ncon(lv), &lo2)
}
switch hv {
case 0:
gmove(&hi1, &hi2)
case 0xffffffff:
gins(x86.AMOVL, ncon(0xffffffff), &hi2)
default:
gmove(&hi1, &hi2)
gins(x86.AORL, ncon(hv), &hi2)
}
}
splitclean()
splitclean()
return
}
gins(x86.AMOVL, &lo1, &ax)
gins(x86.AMOVL, &hi1, &dx)
gins(optoas(int(n.Op), lo1.Type), &lo2, &ax)
gins(optoas(int(n.Op), lo1.Type), &hi2, &dx)
}
if gc.Is64(r.Type) {
splitclean()
}
splitclean()
split64(res, &lo1, &hi1)
gins(x86.AMOVL, &ax, &lo1)
gins(x86.AMOVL, &dx, &hi1)
splitclean()
}
/*
* generate comparison of nl, nr, both 64-bit.
* nl is memory; nr is constant or memory.
*/
func cmp64(nl *gc.Node, nr *gc.Node, op int, likely int, to *obj.Prog) {
var lo1 gc.Node
var hi1 gc.Node
var lo2 gc.Node
var hi2 gc.Node
var rr gc.Node
split64(nl, &lo1, &hi1)
split64(nr, &lo2, &hi2)
// compare most significant word;
// if they differ, we're done.
t := hi1.Type
if nl.Op == gc.OLITERAL || nr.Op == gc.OLITERAL {
gins(x86.ACMPL, &hi1, &hi2)
} else {
gc.Regalloc(&rr, gc.Types[gc.TINT32], nil)
gins(x86.AMOVL, &hi1, &rr)
gins(x86.ACMPL, &rr, &hi2)
gc.Regfree(&rr)
}
var br *obj.Prog
switch op {
default:
gc.Fatal("cmp64 %v %v", gc.Oconv(int(op), 0), t)
// cmp hi
// jne L
// cmp lo
// jeq to
// L:
case gc.OEQ:
br = gc.Gbranch(x86.AJNE, nil, -likely)
// cmp hi
// jne to
// cmp lo
// jne to
case gc.ONE:
gc.Patch(gc.Gbranch(x86.AJNE, nil, likely), to)
// cmp hi
// jgt to
// jlt L
// cmp lo
// jge to (or jgt to)
// L:
case gc.OGE,
gc.OGT:
gc.Patch(gc.Gbranch(optoas(gc.OGT, t), nil, likely), to)
br = gc.Gbranch(optoas(gc.OLT, t), nil, -likely)
// cmp hi
// jlt to
// jgt L
// cmp lo
// jle to (or jlt to)
// L:
case gc.OLE,
gc.OLT:
gc.Patch(gc.Gbranch(optoas(gc.OLT, t), nil, likely), to)
br = gc.Gbranch(optoas(gc.OGT, t), nil, -likely)
}
// compare least significant word
t = lo1.Type
if nl.Op == gc.OLITERAL || nr.Op == gc.OLITERAL {
gins(x86.ACMPL, &lo1, &lo2)
} else {
gc.Regalloc(&rr, gc.Types[gc.TINT32], nil)
gins(x86.AMOVL, &lo1, &rr)
gins(x86.ACMPL, &rr, &lo2)
gc.Regfree(&rr)
}
// jump again
gc.Patch(gc.Gbranch(optoas(op, t), nil, likely), to)
// point first branch down here if appropriate
if br != nil {
gc.Patch(br, gc.Pc)
}
splitclean()
splitclean()
}