[dev.ssa] cmd/compile: decompose 64-bit integer on ARM
Introduce dec64 rules to (generically) decompose 64-bit integer on
32-bit architectures. 64-bit integer is composed/decomposed with
Int64Make/Hi/Lo ops, as for complex types.
The idea of dealing with Add64 is the following:
(Add64 (Int64Make xh xl) (Int64Make yh yl))
->
(Int64Make
(Add32withcarry xh yh (Select0 (Add32carry xl yl)))
(Select1 (Add32carry xl yl)))
where Add32carry returns a tuple (flags,uint32). Select0 and Select1
read the first and the second component of the tuple, respectively.
The two Add32carry will be CSE'd.
Similarly for multiplication, Mul32uhilo returns a tuple (hi, lo).
Also add support of KeepAlive, to fix build after merge.
Tests addressed_ssa.go, array_ssa.go, break_ssa.go, chan_ssa.go,
cmp_ssa.go, ctl_ssa.go, map_ssa.go, and string_ssa.go in
cmd/compile/internal/gc/testdata passed.
Progress on SSA for ARM. Still not complete.
Updates #15365.
Change-Id: I7867c76785a456312de5d8398a6b3f7ca5a4f7ec
Reviewed-on: https://go-review.googlesource.com/23213
Reviewed-by: Keith Randall <khr@golang.org>
diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go
index 9465014..880d3bb 100644
--- a/src/cmd/compile/internal/ssa/gen/genericOps.go
+++ b/src/cmd/compile/internal/ssa/gen/genericOps.go
@@ -173,76 +173,76 @@
{name: "Lrot64", argLength: 1, aux: "Int64"},
// 2-input comparisons
- {name: "Eq8", argLength: 2, commutative: true}, // arg0 == arg1
- {name: "Eq16", argLength: 2, commutative: true},
- {name: "Eq32", argLength: 2, commutative: true},
- {name: "Eq64", argLength: 2, commutative: true},
- {name: "EqPtr", argLength: 2, commutative: true},
- {name: "EqInter", argLength: 2}, // arg0 or arg1 is nil; other cases handled by frontend
- {name: "EqSlice", argLength: 2}, // arg0 or arg1 is nil; other cases handled by frontend
- {name: "Eq32F", argLength: 2},
- {name: "Eq64F", argLength: 2},
+ {name: "Eq8", argLength: 2, commutative: true, typ: "Bool"}, // arg0 == arg1
+ {name: "Eq16", argLength: 2, commutative: true, typ: "Bool"},
+ {name: "Eq32", argLength: 2, commutative: true, typ: "Bool"},
+ {name: "Eq64", argLength: 2, commutative: true, typ: "Bool"},
+ {name: "EqPtr", argLength: 2, commutative: true, typ: "Bool"},
+ {name: "EqInter", argLength: 2, typ: "Bool"}, // arg0 or arg1 is nil; other cases handled by frontend
+ {name: "EqSlice", argLength: 2, typ: "Bool"}, // arg0 or arg1 is nil; other cases handled by frontend
+ {name: "Eq32F", argLength: 2, typ: "Bool"},
+ {name: "Eq64F", argLength: 2, typ: "Bool"},
- {name: "Neq8", argLength: 2, commutative: true}, // arg0 != arg1
- {name: "Neq16", argLength: 2, commutative: true},
- {name: "Neq32", argLength: 2, commutative: true},
- {name: "Neq64", argLength: 2, commutative: true},
- {name: "NeqPtr", argLength: 2, commutative: true},
- {name: "NeqInter", argLength: 2}, // arg0 or arg1 is nil; other cases handled by frontend
- {name: "NeqSlice", argLength: 2}, // arg0 or arg1 is nil; other cases handled by frontend
- {name: "Neq32F", argLength: 2},
+ {name: "Neq8", argLength: 2, commutative: true, typ: "Bool"}, // arg0 != arg1
+ {name: "Neq16", argLength: 2, commutative: true, typ: "Bool"},
+ {name: "Neq32", argLength: 2, commutative: true, typ: "Bool"},
+ {name: "Neq64", argLength: 2, commutative: true, typ: "Bool"},
+ {name: "NeqPtr", argLength: 2, commutative: true, typ: "Bool"},
+ {name: "NeqInter", argLength: 2, typ: "Bool"}, // arg0 or arg1 is nil; other cases handled by frontend
+ {name: "NeqSlice", argLength: 2, typ: "Bool"}, // arg0 or arg1 is nil; other cases handled by frontend
+ {name: "Neq32F", argLength: 2, typ: "Bool"},
{name: "Neq64F", argLength: 2},
- {name: "Less8", argLength: 2}, // arg0 < arg1, signed
- {name: "Less8U", argLength: 2}, // arg0 < arg1, unsigned
- {name: "Less16", argLength: 2},
- {name: "Less16U", argLength: 2},
- {name: "Less32", argLength: 2},
- {name: "Less32U", argLength: 2},
- {name: "Less64", argLength: 2},
- {name: "Less64U", argLength: 2},
- {name: "Less32F", argLength: 2},
- {name: "Less64F", argLength: 2},
+ {name: "Less8", argLength: 2, typ: "Bool"}, // arg0 < arg1, signed
+ {name: "Less8U", argLength: 2, typ: "Bool"}, // arg0 < arg1, unsigned
+ {name: "Less16", argLength: 2, typ: "Bool"},
+ {name: "Less16U", argLength: 2, typ: "Bool"},
+ {name: "Less32", argLength: 2, typ: "Bool"},
+ {name: "Less32U", argLength: 2, typ: "Bool"},
+ {name: "Less64", argLength: 2, typ: "Bool"},
+ {name: "Less64U", argLength: 2, typ: "Bool"},
+ {name: "Less32F", argLength: 2, typ: "Bool"},
+ {name: "Less64F", argLength: 2, typ: "Bool"},
- {name: "Leq8", argLength: 2}, // arg0 <= arg1, signed
- {name: "Leq8U", argLength: 2}, // arg0 <= arg1, unsigned
- {name: "Leq16", argLength: 2},
- {name: "Leq16U", argLength: 2},
- {name: "Leq32", argLength: 2},
- {name: "Leq32U", argLength: 2},
- {name: "Leq64", argLength: 2},
- {name: "Leq64U", argLength: 2},
- {name: "Leq32F", argLength: 2},
- {name: "Leq64F", argLength: 2},
+ {name: "Leq8", argLength: 2, typ: "Bool"}, // arg0 <= arg1, signed
+ {name: "Leq8U", argLength: 2, typ: "Bool"}, // arg0 <= arg1, unsigned
+ {name: "Leq16", argLength: 2, typ: "Bool"},
+ {name: "Leq16U", argLength: 2, typ: "Bool"},
+ {name: "Leq32", argLength: 2, typ: "Bool"},
+ {name: "Leq32U", argLength: 2, typ: "Bool"},
+ {name: "Leq64", argLength: 2, typ: "Bool"},
+ {name: "Leq64U", argLength: 2, typ: "Bool"},
+ {name: "Leq32F", argLength: 2, typ: "Bool"},
+ {name: "Leq64F", argLength: 2, typ: "Bool"},
- {name: "Greater8", argLength: 2}, // arg0 > arg1, signed
- {name: "Greater8U", argLength: 2}, // arg0 > arg1, unsigned
- {name: "Greater16", argLength: 2},
- {name: "Greater16U", argLength: 2},
- {name: "Greater32", argLength: 2},
- {name: "Greater32U", argLength: 2},
- {name: "Greater64", argLength: 2},
- {name: "Greater64U", argLength: 2},
- {name: "Greater32F", argLength: 2},
- {name: "Greater64F", argLength: 2},
+ {name: "Greater8", argLength: 2, typ: "Bool"}, // arg0 > arg1, signed
+ {name: "Greater8U", argLength: 2, typ: "Bool"}, // arg0 > arg1, unsigned
+ {name: "Greater16", argLength: 2, typ: "Bool"},
+ {name: "Greater16U", argLength: 2, typ: "Bool"},
+ {name: "Greater32", argLength: 2, typ: "Bool"},
+ {name: "Greater32U", argLength: 2, typ: "Bool"},
+ {name: "Greater64", argLength: 2, typ: "Bool"},
+ {name: "Greater64U", argLength: 2, typ: "Bool"},
+ {name: "Greater32F", argLength: 2, typ: "Bool"},
+ {name: "Greater64F", argLength: 2, typ: "Bool"},
- {name: "Geq8", argLength: 2}, // arg0 <= arg1, signed
- {name: "Geq8U", argLength: 2}, // arg0 <= arg1, unsigned
- {name: "Geq16", argLength: 2},
- {name: "Geq16U", argLength: 2},
- {name: "Geq32", argLength: 2},
- {name: "Geq32U", argLength: 2},
- {name: "Geq64", argLength: 2},
- {name: "Geq64U", argLength: 2},
- {name: "Geq32F", argLength: 2},
- {name: "Geq64F", argLength: 2},
+ {name: "Geq8", argLength: 2, typ: "Bool"}, // arg0 <= arg1, signed
+ {name: "Geq8U", argLength: 2, typ: "Bool"}, // arg0 <= arg1, unsigned
+ {name: "Geq16", argLength: 2, typ: "Bool"},
+ {name: "Geq16U", argLength: 2, typ: "Bool"},
+ {name: "Geq32", argLength: 2, typ: "Bool"},
+ {name: "Geq32U", argLength: 2, typ: "Bool"},
+ {name: "Geq64", argLength: 2, typ: "Bool"},
+ {name: "Geq64U", argLength: 2, typ: "Bool"},
+ {name: "Geq32F", argLength: 2, typ: "Bool"},
+ {name: "Geq64F", argLength: 2, typ: "Bool"},
// boolean ops
- {name: "AndB", argLength: 2}, // arg0 && arg1 (not shortcircuited)
- {name: "OrB", argLength: 2}, // arg0 || arg1 (not shortcircuited)
- {name: "EqB", argLength: 2}, // arg0 == arg1
- {name: "NeqB", argLength: 2}, // arg0 != arg1
- {name: "Not", argLength: 1}, // !arg0, boolean
+ {name: "AndB", argLength: 2, typ: "Bool"}, // arg0 && arg1 (not shortcircuited)
+ {name: "OrB", argLength: 2, typ: "Bool"}, // arg0 || arg1 (not shortcircuited)
+ {name: "EqB", argLength: 2, typ: "Bool"}, // arg0 == arg1
+ {name: "NeqB", argLength: 2, typ: "Bool"}, // arg0 != arg1
+ {name: "Not", argLength: 1, typ: "Bool"}, // !arg0, boolean
// 1-input ops
{name: "Neg8", argLength: 1}, // -arg0
@@ -416,6 +416,25 @@
{name: "VarKill", argLength: 1, aux: "Sym"}, // aux is a *gc.Node of a variable that is known to be dead. arg0=mem, returns mem
{name: "VarLive", argLength: 1, aux: "Sym"}, // aux is a *gc.Node of a variable that must be kept live. arg0=mem, returns mem
{name: "KeepAlive", argLength: 2, typ: "Mem"}, // arg[0] is a value that must be kept alive until this mark. arg[1]=mem, returns mem
+
+ // Ops for breaking 64-bit operations on 32-bit architectures
+ {name: "Int64Make", argLength: 2, typ: "UInt64"}, // arg0=hi, arg1=lo
+ {name: "Int64Hi", argLength: 1, typ: "UInt32"}, // high 32-bit of arg0
+ {name: "Int64Lo", argLength: 1, typ: "UInt32"}, // low 32-bit of arg0
+
+ {name: "Add32carry", argLength: 2, commutative: true, typ: "(Flags,UInt32)"}, // arg0 + arg1, returns (carry, value)
+ {name: "Add32withcarry", argLength: 3, commutative: true}, // arg0 + arg1 + arg2, arg2=carry (0 or 1)
+
+ {name: "Sub32carry", argLength: 2, typ: "(Flags,UInt32)"}, // arg0 - arg1, returns (carry, value)
+ {name: "Sub32withcarry", argLength: 3}, // arg0 - arg1 - arg2, arg2=carry (0 or 1)
+
+ {name: "Mul32uhilo", argLength: 2, typ: "(UInt32,UInt32)"}, // arg0 * arg1, returns (hi, lo)
+
+ {name: "Signmask", argLength: 1, typ: "Int32"}, // 0 if arg0 >= 0, -1 if arg0 < 0
+
+ // pseudo-ops for breaking Tuple
+ {name: "Select0", argLength: 1}, // the first component of a tuple
+ {name: "Select1", argLength: 1}, // the second component of a tuple
}
// kind control successors implicit exit