| // 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. |
| |
| // x86 register conventions: |
| // - Integer types live in the low portion of registers. |
| // Upper portions are correctly extended. |
| // TODO: reconsider? The current choice means we need no extension for indexing, |
| // but we do need extension for e.g. 32-bit signed adds. |
| // - Boolean types use the low-order byte of a register. Upper bytes are junk. |
| // - We do not use AH,BH,CH,DH registers. |
| // - Floating-point types will live in the low natural slot of an sse2 register. |
| // Unused portions are junk. |
| |
| // Lowering arithmetic |
| (Add <t> x y) && (is64BitInt(t) || isPtr(t)) -> (ADDQ x y) |
| (Add <t> x y) && is32BitInt(t) && !isSigned(t) -> (ADDL x y) |
| (Add <t> x y) && is32BitInt(t) && isSigned(t) -> (MOVLQSX (ADDL <t> x y)) |
| (Add <t> x y) && is16BitInt(t) && !isSigned(t) -> (ADDW x y) |
| (Add <t> x y) && is16BitInt(t) && isSigned(t) -> (MOVWQSX (ADDW <t> x y)) |
| (Add <t> x y) && is8BitInt(t) && !isSigned(t) -> (ADDB x y) |
| (Add <t> x y) && is8BitInt(t) && isSigned(t) -> (MOVBQSX (ADDB <t> x y)) |
| (Sub <t> x y) && is64BitInt(t) -> (SUBQ x y) |
| (Mul <t> x y) && is64BitInt(t) -> (MULQ x y) |
| |
| (MOVLstore ptr (MOVLQSX x) mem) -> (MOVLstore ptr x mem) |
| (MOVWstore ptr (MOVWQSX x) mem) -> (MOVWstore ptr x mem) |
| (MOVBstore ptr (MOVBQSX x) mem) -> (MOVBstore ptr x mem) |
| |
| (Convert <t> x) && t.IsInteger() && x.Type.IsInteger() -> (Copy x) |
| |
| // Lowering shifts |
| // Note: unsigned shifts need to return 0 if shift amount is >= 64. |
| // mask = shift >= 64 ? 0 : 0xffffffffffffffff |
| // result = mask & arg << shift |
| (Lsh <t> x y) && is64BitInt(t) -> |
| (ANDQ (SHLQ <t> x y) (SBBQcarrymask <t> (CMPQconst <TypeFlags> [64] y))) |
| (Rsh <t> x y) && is64BitInt(t) && !t.IsSigned() -> |
| (ANDQ (SHRQ <t> x y) (SBBQcarrymask <t> (CMPQconst <TypeFlags> [64] y))) |
| |
| // Note: signed right shift needs to return 0/-1 if shift amount is >= 64. |
| // if shift > 63 { shift = 63 } |
| // result = arg >> shift |
| (Rsh <t> x y) && is64BitInt(t) && t.IsSigned() -> |
| (SARQ <t> x (CMOVQCC <t> |
| (CMPQconst <TypeFlags> [64] y) |
| (Const <t> [63]) |
| y)) |
| |
| (Less x y) && is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type) -> (SETL (CMPQ <TypeFlags> x y)) |
| |
| (Load <t> ptr mem) && (is64BitInt(t) || isPtr(t)) -> (MOVQload ptr mem) |
| (Load <t> ptr mem) && is32BitInt(t) -> (MOVLload ptr mem) |
| (Load <t> ptr mem) && is16BitInt(t) -> (MOVWload ptr mem) |
| (Load <t> ptr mem) && (t.IsBoolean() || is8BitInt(t)) -> (MOVBload ptr mem) |
| (Store ptr val mem) && (is64BitInt(val.Type) || isPtr(val.Type)) -> (MOVQstore ptr val mem) |
| (Store ptr val mem) && is32BitInt(val.Type) -> (MOVLstore ptr val mem) |
| (Store ptr val mem) && is16BitInt(val.Type) -> (MOVWstore ptr val mem) |
| (Store ptr val mem) && is8BitInt(val.Type) -> (MOVBstore ptr val mem) |
| |
| // checks |
| (IsNonNil p) -> (SETNE (TESTQ <TypeFlags> p p)) |
| (IsInBounds idx len) -> (SETB (CMPQ <TypeFlags> idx len)) |
| |
| (Move [size] dst src mem) -> (REPMOVSB dst src (Const <TypeUInt64> [size]) mem) |
| |
| (OffPtr [off] ptr) -> (ADDQconst [off] ptr) |
| |
| (Const <t> [val]) && t.IsInteger() -> (MOVQconst [val]) |
| |
| (Addr {sym} base) -> (LEAQ {sym} base) |
| |
| // block rewrites |
| (If (SETL cmp) yes no) -> (LT cmp yes no) |
| (If (SETNE cmp) yes no) -> (NE cmp yes no) |
| (If (SETB cmp) yes no) -> (ULT cmp yes no) |
| (If cond yes no) && cond.Op == OpAMD64MOVBload -> (NE (TESTB <TypeFlags> cond cond) yes no) |
| |
| (StaticCall {target} mem) -> (CALLstatic {target} mem) |
| (ClosureCall entry closure mem) -> (CALLclosure entry closure mem) |
| |
| // Rules below here apply some simple optimizations after lowering. |
| // TODO: Should this be a separate pass? |
| |
| // fold constants into instructions |
| (ADDQ x (MOVQconst [c])) -> (ADDQconst [c] x) // TODO: restrict c to int32 range? |
| (ADDQ (MOVQconst [c]) x) -> (ADDQconst [c] x) |
| (SUBQ x (MOVQconst [c])) -> (SUBQconst x [c]) |
| (SUBQ <t> (MOVQconst [c]) x) -> (NEGQ (SUBQconst <t> x [c])) |
| (MULQ x (MOVQconst [c])) && c == int64(int32(c)) -> (MULQconst [c] x) |
| (MULQ (MOVQconst [c]) x) -> (MULQconst [c] x) |
| (ANDQ x (MOVQconst [c])) -> (ANDQconst [c] x) |
| (ANDQ (MOVQconst [c]) x) -> (ANDQconst [c] x) |
| (SHLQ x (MOVQconst [c])) -> (SHLQconst [c] x) |
| (SHRQ x (MOVQconst [c])) -> (SHRQconst [c] x) |
| (SARQ x (MOVQconst [c])) -> (SARQconst [c] x) |
| (CMPQ x (MOVQconst [c])) -> (CMPQconst x [c]) |
| (CMPQ (MOVQconst [c]) x) -> (InvertFlags (CMPQconst <TypeFlags> x [c])) |
| |
| // strength reduction |
| // TODO: do this a lot more generically |
| (MULQconst [8] x) -> (SHLQconst [3] x) |
| (MULQconst [64] x) -> (SHLQconst [5] x) |
| |
| // fold add/shift into leaq |
| (ADDQ x (SHLQconst [3] y)) -> (LEAQ8 x y) |
| (ADDQconst [c] (LEAQ8 [d] x y)) -> (LEAQ8 [addOff(c, d)] x y) |
| |
| // reverse ordering of compare instruction |
| (SETL (InvertFlags x)) -> (SETG x) |
| (SETG (InvertFlags x)) -> (SETL x) |
| |
| // fold constants into memory operations |
| // Note that this is not always a good idea because if not all the uses of |
| // the ADDQconst get eliminated, we still have to compute the ADDQconst and we now |
| // have potentially two live values (ptr and (ADDQconst [off] ptr)) instead of one. |
| // Nevertheless, let's do it! |
| (MOVQload [off1] (ADDQconst [off2] ptr) mem) -> (MOVQload [addOff(off1, off2)] ptr mem) |
| (MOVQstore [off1] (ADDQconst [off2] ptr) val mem) -> (MOVQstore [addOff(off1, off2)] ptr val mem) |
| |
| (MOVQload [off1] {sym1} (LEAQ [off2] {sym2} base) mem) && (sym1 == nil || sym2 == nil) -> |
| (MOVQload [addOff(off1,off2)] {mergeSym(sym1,sym2)} base mem) |
| (MOVQstore [off1] {sym1} (LEAQ [off2] {sym2} base) val mem) && (sym1 == nil || sym2 == nil) -> |
| (MOVQstore [addOff(off1,off2)] {mergeSym(sym1,sym2)} base val mem) |
| |
| // indexed loads and stores |
| (MOVQload [off1] (LEAQ8 [off2] ptr idx) mem) -> (MOVQloadidx8 [addOff(off1, off2)] ptr idx mem) |
| (MOVQstore [off1] (LEAQ8 [off2] ptr idx) val mem) -> (MOVQstoreidx8 [addOff(off1, off2)] ptr idx val mem) |
| |
| (MOVQloadidx8 [off1] (ADDQconst [off2] ptr) idx mem) -> (MOVQloadidx8 [addOff(off1, off2)] ptr idx mem) |
| (MOVQstoreidx8 [off1] (ADDQconst [off2] ptr) idx val mem) -> (MOVQstoreidx8 [addOff(off1, off2)] ptr idx val mem) |
| |
| (ADDQconst [0] x) -> (Copy x) |
| |
| // Absorb InvertFlags into branches. |
| (LT (InvertFlags cmp) yes no) -> (GT cmp yes no) |
| (GT (InvertFlags cmp) yes no) -> (LT cmp yes no) |
| (LE (InvertFlags cmp) yes no) -> (GE cmp yes no) |
| (GE (InvertFlags cmp) yes no) -> (LE cmp yes no) |
| (ULT (InvertFlags cmp) yes no) -> (UGT cmp yes no) |
| (UGT (InvertFlags cmp) yes no) -> (ULT cmp yes no) |
| (ULE (InvertFlags cmp) yes no) -> (UGE cmp yes no) |
| (UGE (InvertFlags cmp) yes no) -> (ULE cmp yes no) |
| (EQ (InvertFlags cmp) yes no) -> (EQ cmp yes no) |
| (NE (InvertFlags cmp) yes no) -> (NE cmp yes no) |
| |
| // get rid of >=64 code for constant shifts |
| (SBBQcarrymask (CMPQconst [c] (MOVQconst [d]))) && inBounds(d, c) -> (Const [-1]) |
| (SBBQcarrymask (CMPQconst [c] (MOVQconst [d]))) && !inBounds(d, c) -> (Const [0]) |
| (ANDQconst [0] _) -> (MOVQconst [0]) |
| (ANDQconst [-1] x) -> (Copy x) |
| (CMOVQCC (CMPQconst [c] (MOVQconst [d])) _ x) && inBounds(d, c) -> (Copy x) |
| (CMOVQCC (CMPQconst [c] (MOVQconst [d])) x _) && !inBounds(d, c) -> (Copy x) |