}
}
+ case AMOVW, AMOVWZ:
+ // Note, for backwards compatibility, MOVW $const, Rx and MOVWZ $const, Rx are identical.
+ if p.From.Type == obj.TYPE_CONST && p.From.Offset != 0 && p.From.Offset&0xFFFF == 0 {
+ // This is a constant shifted 16 bits to the left, convert it to ADDIS/ORIS $const,...
+ p.As = AADDIS
+ // Use ORIS for large constants which should not be sign extended.
+ if p.From.Offset >= 0x80000000 {
+ p.As = AORIS
+ }
+ p.Reg = REG_R0
+ p.From.Offset >>= 16
+ }
+
case AMOVD:
+ // Skip this opcode if it is not a constant load.
+ if p.From.Type != obj.TYPE_CONST || p.From.Name != obj.NAME_NONE || p.From.Reg != 0 {
+ break
+ }
+
// 32b constants (signed and unsigned) can be generated via 1 or 2 instructions. They can be assembled directly.
isS32 := int64(int32(p.From.Offset)) == p.From.Offset
isU32 := uint64(uint32(p.From.Offset)) == uint64(p.From.Offset)
-
// If prefixed instructions are supported, a 34b signed constant can be generated by one pli instruction.
isS34 := pfxEnabled && (p.From.Offset<<30)>>30 == p.From.Offset
- if p.From.Type == obj.TYPE_CONST && p.From.Name == obj.NAME_NONE && p.From.Reg == 0 && !isS32 && !isU32 && !isS34 {
+ // Try converting MOVD $const,Rx into ADDIS/ORIS $s32>>16,R0,Rx
+ switch {
+ case isS32 && p.From.Offset&0xFFFF == 0 && p.From.Offset != 0:
+ p.As = AADDIS
+ p.From.Offset >>= 16
+ p.Reg = REG_R0
+
+ case isU32 && p.From.Offset&0xFFFF == 0 && p.From.Offset != 0:
+ p.As = AORIS
+ p.From.Offset >>= 16
+ p.Reg = REG_R0
+
+ case isS32 || isU32 || isS34:
+ // The assembler can generate this opcode in 1 (on Power10) or 2 opcodes.
+
+ // Otherwise, see if the large constant can be generated with 2 instructions. If not, load it from memory.
+ default:
// Is this a shifted 16b constant? If so, rewrite it to avoid a creating and loading a constant.
val := p.From.Offset
shift := bits.TrailingZeros64(uint64(val))
q.To = p.To
p.From.Offset >>= shift
p = q
- // Is this constant a mask value? If so, generate MOVD $-1, Rto; RLDIC Rto, ^me, mb, Rto
} else if isPPC64DoublewordRotateMask(val) {
+ // This constant is a mask value, generate MOVD $-1, Rto; RLDIC Rto, ^me, mb, Rto
mb, me := encodePPC64RLDCMask(val)
q := obj.Appendp(p, c.newprog)
q.As = ARLDC
p.As = AADD
}
+ // Rewrite ADD/OR/XOR/ANDCC $const,... forms into ADDIS/ORIS/XORIS/ANDISCC
+ case AADD:
+ // AADD can encode signed 34b values, ensure it is a valid signed 32b integer too.
+ if p.From.Type == obj.TYPE_CONST && p.From.Offset&0xFFFF == 0 && int64(int32(p.From.Offset)) == p.From.Offset && p.From.Offset != 0 {
+ p.As = AADDIS
+ p.From.Offset >>= 16
+ }
+ case AOR:
+ if p.From.Type == obj.TYPE_CONST && uint64(p.From.Offset)&0xFFFFFFFF0000FFFF == 0 && p.From.Offset != 0 {
+ p.As = AORIS
+ p.From.Offset >>= 16
+ }
+ case AXOR:
+ if p.From.Type == obj.TYPE_CONST && uint64(p.From.Offset)&0xFFFFFFFF0000FFFF == 0 && p.From.Offset != 0 {
+ p.As = AXORIS
+ p.From.Offset >>= 16
+ }
+ case AANDCC:
+ if p.From.Type == obj.TYPE_CONST && uint64(p.From.Offset)&0xFFFFFFFF0000FFFF == 0 && p.From.Offset != 0 {
+ p.As = AANDISCC
+ p.From.Offset >>= 16
+ }
+
// To maintain backwards compatibility, we accept some 4 argument usage of
// several opcodes which was likely not intended, but did work. These are not
// added to optab to avoid the chance this behavior might be used with newer