Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions amd64_lower_arith.go
Original file line number Diff line number Diff line change
Expand Up @@ -1495,6 +1495,40 @@ func (c *amd64Ctx) lowerArith(op Op, ins Instr) (ok bool, terminated bool, err e
fmt.Fprintf(c.b, " %%%s = zext i32 %%%s to i64\n", z, rot)
return true, false, c.storeReg(dst, "%"+z)

case "NOTB":
// NOT does not modify flags. For a register operand, only the selected
// low byte is changed and the remaining register bits are preserved.
if len(ins.Args) != 1 {
return true, false, fmt.Errorf("amd64 NOTB expects one operand: %q", ins.Raw)
}
switch ins.Args[0].Kind {
case OpReg:
r := ins.Args[0].Reg
v64, err := c.loadReg(r)
if err != nil {
return true, false, err
}
v8 := c.newTmp()
fmt.Fprintf(c.b, " %%%s = trunc i64 %s to i8\n", v8, v64)
not := c.newTmp()
fmt.Fprintf(c.b, " %%%s = xor i8 %%%s, -1\n", not, v8)
return true, false, c.storeRegSized(r, I8, "%"+not)
case OpMem:
addr, err := c.addrFromMem(ins.Args[0].Mem)
if err != nil {
return true, false, err
}
p := c.ptrFromAddrI64(addr)
load := c.newTmp()
fmt.Fprintf(c.b, " %%%s = load i8, ptr %s, align 1\n", load, p)
not := c.newTmp()
fmt.Fprintf(c.b, " %%%s = xor i8 %%%s, -1\n", not, load)
fmt.Fprintf(c.b, " store i8 %%%s, ptr %s, align 1\n", not, p)
return true, false, nil
default:
return true, false, fmt.Errorf("amd64 NOTB expects reg or mem: %q", ins.Raw)
}

case "NOTL":
// 32-bit bitwise NOT, result zero-extended to 64-bit.
if len(ins.Args) != 1 || ins.Args[0].Kind != OpReg {
Expand Down
2 changes: 2 additions & 0 deletions amd64_lower_branch.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ func (c *amd64Ctx) callSym(symOp Operand) error {
// a known no-op runtime scheduler hook above.
return fmt.Errorf("amd64 call missing signature for %q", callee)
}
callee = funcSigSymbol(callee, csig)

args := make([]string, 0, len(csig.Args))
for i := 0; i < len(csig.Args); i++ {
Expand Down Expand Up @@ -350,6 +351,7 @@ func (c *amd64Ctx) tailCallAndRet(symOp Operand) error {
csig = c.sig
csig.Name = callee
}
callee = funcSigSymbol(callee, csig)

args := make([]string, 0, len(csig.Args))
for i := 0; i < len(csig.Args); i++ {
Expand Down
37 changes: 36 additions & 1 deletion amd64_new_opfamilies_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,42 @@

package plan9asm

import "testing"
import (
"strings"
"testing"
)

func TestTranslateAMD64NOTB(t *testing.T) {
src := `
TEXT notb(SB),NOSPLIT,$0-0
MOVQ $0x1234, R11
NOTB R11
NOTB 3(AX)
RET
`
file, err := Parse(ArchAMD64, src)
if err != nil {
t.Fatal(err)
}
ir, err := Translate(file, Options{
TargetTriple: "x86_64-unknown-linux-gnu",
Sigs: map[string]FuncSig{
"notb": {Name: "notb", Ret: Void},
},
Goarch: "amd64",
})
if err != nil {
t.Fatal(err)
}
if got := strings.Count(ir, "xor i8"); got != 2 {
t.Fatalf("NOTB xor count = %d, want 2:\n%s", got, ir)
}
for _, want := range []string{"and i64", "or i64", "load i8", "store i8"} {
if !strings.Contains(ir, want) {
t.Fatalf("NOTB output missing %q:\n%s", want, ir)
}
}
}

func TestTranslateAMD64SHA1Family(t *testing.T) {
src := `
Expand Down
2 changes: 1 addition & 1 deletion arm64_blocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func arm64SplitBlocks(fn Func) []arm64Block {
op = op[:dot]
}
switch Op(op) {
case "B", "JMP", "BEQ", "BNE", "BLO", "BHI", "BLT", "BGE", "BLE", "BGT", "BHS", "BLS",
case "B", "JMP", "BEQ", "BNE", "BLO", "BHI", "BLT", "BGE", "BLE", "BGT", "BHS", "BLS", "BMI", "BPL",
"BCC", "CBZ", "CBNZ", "TBZ", "TBNZ":
return true
default:
Expand Down
18 changes: 18 additions & 0 deletions arm64_ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,18 @@ func arm64ParseVReg(r Reg) (idx int, ok bool) {
return n, true
}

func arm64ParseFReg(r Reg) (idx int, ok bool) {
s := strings.ToUpper(strings.TrimSpace(string(r)))
if !strings.HasPrefix(s, "F") {
return 0, false
}
n, err := strconv.Atoi(strings.TrimPrefix(s, "F"))
if err != nil || n < 0 || n > 31 {
return 0, false
}
return n, true
}

func (c *arm64Ctx) scanUsedRegs() {
markReg := func(r Reg) {
if r == "" {
Expand All @@ -114,6 +126,12 @@ func (c *arm64Ctx) scanUsedRegs() {
c.usedVRegs[idx] = true
return
}
// ARM64 F registers alias the corresponding 128-bit V registers. Keep
// the scalar slot for existing FP lowering and make the vector slot
// available to pair loads such as FLDPQ (F0, F1).
if idx, ok := arm64ParseFReg(r); ok {
c.usedVRegs[idx] = true
}
c.usedRegs[r] = true
}
markOp := func(op Operand) {
Expand Down
4 changes: 4 additions & 0 deletions arm64_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@ func (c *arm64Ctx) condValue(cond string) (string, error) {
return and(not(z), eq(n, v)), nil
case "LE":
return or(z, xor(n, v)), nil
case "MI":
return n, nil
case "PL":
return not(n), nil
default:
return "", fmt.Errorf("arm64: unsupported condition %q", cond)
}
Expand Down
55 changes: 55 additions & 0 deletions arm64_helper_edge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,51 @@ func arm64MemOp(base Reg, off int64) Operand {
}
func arm64RegListOp(regs ...Reg) Operand { return Operand{Kind: OpRegList, RegList: regs} }

func TestARM64FPVectorPairValidation(t *testing.T) {
if idx, ok := arm64ParseFReg("F31"); !ok || idx != 31 {
t.Fatalf("arm64ParseFReg(F31) = (%d, %v)", idx, ok)
}
for _, r := range []Reg{"R0", "F32", "Fbad"} {
if _, ok := arm64ParseFReg(r); ok {
t.Fatalf("arm64ParseFReg(%s) unexpectedly succeeded", r)
}
}

c, _ := newARM64CtxWithFuncForTest(t, Func{}, FuncSig{Name: "example.vecerrors", Ret: Void}, nil)
if ok, _, err := c.lowerVec("FLDPQ", false, Instr{Raw: "FLDPQ"}); !ok || err == nil {
t.Fatalf("invalid FLDPQ = (%v, %v)", ok, err)
}
if ok, _, err := c.lowerVec("FLDPQ", false, Instr{
Raw: "FLDPQ (BAD), (F0, F1)",
Args: []Operand{arm64MemOp("BAD", 0), arm64RegListOp("F0", "F1")},
}); !ok || err == nil {
t.Fatalf("FLDPQ with bad base = (%v, %v)", ok, err)
}
if ok, _, err := c.lowerVec("FLDPQ", false, Instr{
Raw: "FLDPQ (R1), (R0, R1)",
Args: []Operand{arm64MemOp("R1", 0), arm64RegListOp("R0", "R1")},
}); !ok || err == nil {
t.Fatalf("FLDPQ with GPR pair = (%v, %v)", ok, err)
}
if ok, _, err := c.lowerVec("FLDPQ", false, Instr{
Raw: "FLDPQ (R1), (F0, F1)",
Args: []Operand{arm64MemOp("R1", 0), arm64RegListOp("F0", "F1")},
}); !ok || err == nil {
t.Fatalf("FLDPQ without vector slots = (%v, %v)", ok, err)
}

for _, ins := range []Instr{
{Raw: "VMOVI"},
{Raw: "VMOVI $256, V0.B16", Args: []Operand{arm64ImmOp(256), arm64RegOp("V0.B16")}},
{Raw: "VMOVI $1, V0.D2", Args: []Operand{arm64ImmOp(1), arm64RegOp("V0.D2")}},
{Raw: "VMOVI $1, V0.B8", Args: []Operand{arm64ImmOp(1), arm64RegOp("V0.B8")}},
} {
if ok, _, err := c.lowerVec("VMOVI", false, ins); !ok || err == nil {
t.Fatalf("invalid %q = (%v, %v)", ins.Raw, ok, err)
}
}
}

func mustLowerARM64(t *testing.T, kind string, ins Instr, ok bool, err error) {
t.Helper()
if err != nil {
Expand Down Expand Up @@ -244,6 +289,7 @@ func TestARM64ArithmeticCoverage(t *testing.T) {
{Op: "LSL", Args: []Operand{arm64ImmOp(3), arm64RegOp("R6"), arm64RegOp("R7")}, Raw: "LSL $3, R6, R7"},
{Op: "LSR", Args: []Operand{arm64RegOp("R0"), arm64RegOp("R7"), arm64RegOp("R8")}, Raw: "LSR R0, R7, R8"},
{Op: "LSLW", Args: []Operand{arm64RegOp("R1"), arm64RegOp("R8"), arm64RegOp("R9")}, Raw: "LSLW R1, R8, R9"},
{Op: "LSRW", Args: []Operand{arm64ImmOp(2), arm64RegOp("R9"), arm64RegOp("R10")}, Raw: "LSRW $2, R9, R10"},
{Op: "ASR", Args: []Operand{arm64ImmOp(2), arm64RegOp("R9"), arm64RegOp("R10")}, Raw: "ASR $2, R9, R10"},
{Op: "UDIV", Args: []Operand{arm64RegOp("R2"), arm64RegOp("R10"), arm64RegOp("R0")}, Raw: "UDIV R2, R10, R0"},
{Op: "EXTR", Args: []Operand{arm64ImmOp(9), arm64RegOp("R0"), arm64RegOp("R1"), arm64RegOp("R2")}, Raw: "EXTR $9, R0, R1, R2"},
Expand All @@ -268,6 +314,12 @@ func TestARM64ArithmeticCoverage(t *testing.T) {
if got, err := c.condValue("HI"); err != nil || got == "" {
t.Fatalf("condValue(HI) = (%q, %v)", got, err)
}
if got, err := c.condValue("MI"); err != nil || got == "" {
t.Fatalf("condValue(MI) = (%q, %v)", got, err)
}
if got, err := c.condValue("PL"); err != nil || got == "" {
t.Fatalf("condValue(PL) = (%q, %v)", got, err)
}
if _, err := (&arm64Ctx{}).condValue("EQ"); err == nil {
t.Fatalf("condValue without flags unexpectedly succeeded")
}
Expand All @@ -286,6 +338,7 @@ func TestARM64ArithmeticCoverage(t *testing.T) {
`asm sideeffect "mrs $0, TPIDR_EL0"`,
`asm sideeffect "msr S3_3_C4_C2_5, $0"`,
"lshr i64",
"lshr i32",
"shl i64",
"ashr i64",
"udiv i64",
Expand Down Expand Up @@ -480,6 +533,8 @@ func TestARM64DataVectorAndBranchCoverage(t *testing.T) {
{Op: "BLE", Args: []Operand{arm64IdentOp("done")}, Raw: "BLE done"},
{Op: "BCC", Args: []Operand{arm64IdentOp("done")}, Raw: "BCC done"},
{Op: "BCS", Args: []Operand{arm64IdentOp("done")}, Raw: "BCS done"},
{Op: "BMI", Args: []Operand{arm64IdentOp("done")}, Raw: "BMI done"},
{Op: "BPL", Args: []Operand{arm64IdentOp("done")}, Raw: "BPL done"},
{Op: "CBZ", Args: []Operand{arm64RegOp("R2"), arm64IdentOp("done")}, Raw: "CBZ R2, done"},
{Op: "CBNZ", Args: []Operand{arm64RegOp("R3"), arm64MemOp(PC, 4)}, Raw: "CBNZ R3, 4(PC)"},
{Op: "TBZ", Args: []Operand{arm64ImmOp(1), arm64RegOp("R4"), arm64IdentOp("done")}, Raw: "TBZ $1, R4, done"},
Expand Down
35 changes: 28 additions & 7 deletions arm64_lower_arith.go
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,23 @@ func (c *arm64Ctx) lowerArith(op Op, ins Instr) (ok bool, terminated bool, err e
}
return true, false, nil

case "TST":
if len(ins.Args) != 2 {
return true, false, fmt.Errorf("arm64 TST expects 2 operands: %q", ins.Raw)
}
a, err := c.eval64(ins.Args[0], false)
if err != nil {
return true, false, err
}
bval, err := c.eval64(ins.Args[1], false)
if err != nil {
return true, false, err
}
res := c.newTmp()
fmt.Fprintf(c.b, " %%%s = and i64 %s, %s\n", res, bval, a)
c.setFlagsLogic("%" + res)
return true, false, nil

case "ANDSW":
if len(ins.Args) != 2 && len(ins.Args) != 3 {
return true, false, fmt.Errorf("arm64 ANDSW expects 2 or 3 operands: %q", ins.Raw)
Expand Down Expand Up @@ -796,24 +813,24 @@ func (c *arm64Ctx) lowerArith(op Op, ins Instr) (ok bool, terminated bool, err e
}
return true, false, c.storeReg(dstReg, "%"+t)

case "LSLW":
// LSLW shift, dstReg or LSLW shift, srcReg, dstReg
case "LSLW", "LSRW":
// LSLW/LSRW shift, dstReg or LSLW/LSRW shift, srcReg, dstReg
if len(ins.Args) != 2 && len(ins.Args) != 3 {
return true, false, fmt.Errorf("arm64 LSLW expects 2 or 3 operands: %q", ins.Raw)
return true, false, fmt.Errorf("arm64 %s expects 2 or 3 operands: %q", op, ins.Raw)
}
var srcReg Reg
var dstReg Reg
var sh Operand
if len(ins.Args) == 2 {
sh = ins.Args[0]
if ins.Args[1].Kind != OpReg {
return true, false, fmt.Errorf("arm64 LSLW expects shift, dstReg: %q", ins.Raw)
return true, false, fmt.Errorf("arm64 %s expects shift, dstReg: %q", op, ins.Raw)
}
srcReg, dstReg = ins.Args[1].Reg, ins.Args[1].Reg
} else {
sh = ins.Args[0]
if ins.Args[1].Kind != OpReg || ins.Args[2].Kind != OpReg {
return true, false, fmt.Errorf("arm64 LSLW expects shift, srcReg, dstReg: %q", ins.Raw)
return true, false, fmt.Errorf("arm64 %s expects shift, srcReg, dstReg: %q", op, ins.Raw)
}
srcReg, dstReg = ins.Args[1].Reg, ins.Args[2].Reg
}
Expand All @@ -838,10 +855,14 @@ func (c *arm64Ctx) lowerArith(op Op, ins Instr) (ok bool, terminated bool, err e
fmt.Fprintf(c.b, " %%%s = and i32 %%%s, 31\n", m, st)
sh32 = "%" + m
default:
return true, false, fmt.Errorf("arm64 LSLW unsupported shift operand: %q", ins.Raw)
return true, false, fmt.Errorf("arm64 %s unsupported shift operand: %q", op, ins.Raw)
}
t := c.newTmp()
fmt.Fprintf(c.b, " %%%s = shl i32 %%%s, %s\n", t, src32, sh32)
if op == "LSLW" {
fmt.Fprintf(c.b, " %%%s = shl i32 %%%s, %s\n", t, src32, sh32)
} else {
fmt.Fprintf(c.b, " %%%s = lshr i32 %%%s, %s\n", t, src32, sh32)
}
z := c.newTmp()
fmt.Fprintf(c.b, " %%%s = zext i32 %%%s to i64\n", z, t)
return true, false, c.storeReg(dstReg, "%"+z)
Expand Down
8 changes: 7 additions & 1 deletion arm64_lower_branch.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func (c *arm64Ctx) lowerBranch(bi int, op Op, ins Instr, emitBr arm64EmitBr, emi
emitBr(tgt)
return true, true, nil

case "BEQ", "BNE", "BLO", "BLT", "BHI", "BHS", "BLS", "BGE", "BGT", "BLE", "BCC", "BCS":
case "BEQ", "BNE", "BLO", "BLT", "BHI", "BHS", "BLS", "BGE", "BGT", "BLE", "BCC", "BCS", "BMI", "BPL":
if len(ins.Args) != 1 {
return true, false, fmt.Errorf("arm64 %s expects label: %q", op, ins.Raw)
}
Expand Down Expand Up @@ -140,6 +140,10 @@ func (c *arm64Ctx) lowerBranch(bi int, op Op, ins Instr, emitBr arm64EmitBr, emi
cond = "GT"
case "BLE":
cond = "LE"
case "BMI":
cond = "MI"
case "BPL":
cond = "PL"
}
if err := emitCondBr(cond, tgt, fall); err != nil {
return true, false, err
Expand Down Expand Up @@ -301,6 +305,7 @@ func (c *arm64Ctx) callSym(symOp Operand) error {
// Default for external runtime helpers not discovered in this asm file.
csig = FuncSig{Name: callee, Ret: Void}
}
callee = funcSigSymbol(callee, csig)
args := make([]string, 0, len(csig.Args))
regCursor := 0
for i := 0; i < len(csig.Args); i++ {
Expand Down Expand Up @@ -375,6 +380,7 @@ func (c *arm64Ctx) tailCallAndRet(symOp Operand) error {
csig = c.sig
csig.Name = callee
}
callee = funcSigSymbol(callee, csig)

args := make([]string, 0, len(csig.Args))
regCursor := 0
Expand Down
Loading
Loading