| ... |
... |
@@ -356,13 +356,10 @@ type InstrBlock |
|
356
|
356
|
--
|
|
357
|
357
|
data Register
|
|
358
|
358
|
= Fixed Format Reg InstrBlock
|
|
|
359
|
+ -- ^ It can be unsafe to clobber the result reg, as it might map to a
|
|
|
360
|
+ -- local variable.
|
|
359
|
361
|
| Any Format (Reg -> InstrBlock)
|
|
360
|
|
-
|
|
361
|
|
--- | Sometimes we need to change the Format of a register. Primarily during
|
|
362
|
|
--- conversion.
|
|
363
|
|
-swizzleRegisterRep :: Format -> Register -> Register
|
|
364
|
|
-swizzleRegisterRep format (Fixed _ reg code) = Fixed format reg code
|
|
365
|
|
-swizzleRegisterRep format (Any _ codefn) = Any format codefn
|
|
|
362
|
+ -- ^ A destination the caller decides, prevents redundant moves
|
|
366
|
363
|
|
|
367
|
364
|
-- | Grab the Reg for a CmmReg
|
|
368
|
365
|
getRegisterReg :: Platform -> CmmReg -> Reg
|
| ... |
... |
@@ -370,8 +367,9 @@ getRegisterReg :: Platform -> CmmReg -> Reg |
|
370
|
367
|
getRegisterReg _ (CmmLocal (LocalReg u pk))
|
|
371
|
368
|
= RegVirtual $ mkVirtualReg u (cmmTypeFormat pk)
|
|
372
|
369
|
|
|
373
|
|
-getRegisterReg platform (CmmGlobal reg@(GlobalRegUse mid _))
|
|
374
|
|
- = case globalRegMaybe platform mid of
|
|
|
370
|
+getRegisterReg platform (CmmGlobal reg@(GlobalRegUse mid ty))
|
|
|
371
|
+ = assert (formatInBytes (cmmTypeFormat ty) >= 4) $
|
|
|
372
|
+ case globalRegMaybe platform mid of
|
|
375
|
373
|
Just reg -> RegReal reg
|
|
376
|
374
|
Nothing -> pprPanic "getRegisterReg-memory" (ppr $ CmmGlobal reg)
|
|
377
|
375
|
-- By this stage, the only MagicIds remaining should be the
|
| ... |
... |
@@ -382,11 +380,17 @@ getRegisterReg platform (CmmGlobal reg@(GlobalRegUse mid _)) |
|
382
|
380
|
-- -----------------------------------------------------------------------------
|
|
383
|
381
|
-- General things for putting together code sequences
|
|
384
|
382
|
|
|
385
|
|
--- | The dual to getAnyReg: compute an expression into a register, but
|
|
386
|
|
--- we don't mind which one it is.
|
|
|
383
|
+-- | Computes the `Register` value into a concrete register, but we can't pick which one.
|
|
|
384
|
+-- This means the register might be mapped to a global or local variable and
|
|
|
385
|
+-- we can only mutate the result reg in place if we know the Cmm expression can't
|
|
|
386
|
+-- refer to local or global variables.
|
|
387
|
387
|
getSomeReg :: CmmExpr -> NatM (Reg, Format, InstrBlock)
|
|
388
|
388
|
getSomeReg expr = do
|
|
389
|
389
|
r <- getRegister expr
|
|
|
390
|
+ someReg r
|
|
|
391
|
+
|
|
|
392
|
+someReg :: Register -> NatM (Reg, Format, InstrBlock)
|
|
|
393
|
+someReg r =
|
|
390
|
394
|
case r of
|
|
391
|
395
|
Any rep code -> do
|
|
392
|
396
|
tmp <- getNewRegNat rep
|
| ... |
... |
@@ -647,28 +651,38 @@ opRegWidth W16 = W32 -- w |
|
647
|
651
|
opRegWidth W8 = W32 -- w
|
|
648
|
652
|
opRegWidth w = pprPanic "opRegWidth" (text "Unsupported width" <+> ppr w)
|
|
649
|
653
|
|
|
650
|
|
--- Note [Signed arithmetic on AArch64]
|
|
651
|
|
--- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
652
|
|
--- Handling signed arithmetic on sub-word-size values on AArch64 is a bit
|
|
653
|
|
--- tricky as Cmm's type system does not capture signedness. While 32-bit values
|
|
654
|
|
--- are fairly easy to handle due to AArch64's 32-bit instruction variants
|
|
655
|
|
--- (denoted by use of %wN registers), 16- and 8-bit values require quite some
|
|
656
|
|
--- care.
|
|
|
654
|
+-- Note [Subword operations on AArch64]
|
|
|
655
|
+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
656
|
+-- Handling subword operations on AArch64 is a bit tricky. 32-bit values are fairly
|
|
|
657
|
+-- easy to handle due to AArch64's 32-bit instruction variants. 16- and 8-bit
|
|
|
658
|
+-- values require quite some care. The platform doesn't provide operations at
|
|
|
659
|
+-- widths below 32bit. Which means we have to simulate them using wider operations.
|
|
|
660
|
+-- Signed arithmetic on sub-word-size values on AArch64 is a bit tricky as Cmm's
|
|
|
661
|
+-- type system does not capture signedness. If we have a 8 bit value the high
|
|
|
662
|
+-- bits could be sign or zero extended with no easy way to tell.
|
|
657
|
663
|
--
|
|
658
|
|
--- We handle 16-and 8-bit values by using the 32-bit operations and
|
|
|
664
|
+-- To work around this handle 16-and 8-bit values by using the 32-bit operations and
|
|
659
|
665
|
-- sign-/zero-extending operands and truncate results as necessary. For
|
|
660
|
666
|
-- simplicity we maintain the invariant that a register containing a
|
|
661
|
667
|
-- sub-word-size value always contains the zero-extended form of that value
|
|
662
|
668
|
-- in between operations.
|
|
663
|
669
|
--
|
|
664
|
|
--- IMPORTANT: this invariant only holds within a single expression tree as
|
|
665
|
|
--- generated by the NCG (via truncateReg after each sub-word operation). It
|
|
666
|
|
--- does NOT hold at function entry points or across basic block boundaries,
|
|
667
|
|
--- because the GHC calling convention does not guarantee that callers
|
|
668
|
|
--- zero-extend sub-word arguments. Therefore, any operation that is sensitive
|
|
669
|
|
--- to the upper bits of its input (e.g. unsigned right shift, unsigned
|
|
670
|
|
--- division) must explicitly zero- or sign-extend its operands rather than
|
|
671
|
|
--- assuming they are already extended.
|
|
|
670
|
+-- Concretely we establish this invariant on every input into the function for which
|
|
|
671
|
+-- we generate code for in the NCG. This means:
|
|
|
672
|
+-- * Global STG register access
|
|
|
673
|
+-- * memory reads
|
|
|
674
|
+-- * function arguments
|
|
|
675
|
+-- * ffi results
|
|
|
676
|
+-- * function call results
|
|
|
677
|
+-- * results from any subexpression
|
|
|
678
|
+--
|
|
|
679
|
+-- This means we can assume the invariant when generated code for expression trees
|
|
|
680
|
+-- or machops reading local variables, avoiding (some) redundant extensions. But
|
|
|
681
|
+-- we have to take great care to uphold the invariant when computing new values.
|
|
|
682
|
+--
|
|
|
683
|
+-- We used to do the inverse. Re-establish the invariant for any operation that
|
|
|
684
|
+-- is sensitive to values in the high bits. But that turned out to produce worse
|
|
|
685
|
+-- code and wasn't any less likely to result in new bugs in practice.
|
|
672
|
686
|
--
|
|
673
|
687
|
-- For instance, consider the program,
|
|
674
|
688
|
--
|
| ... |
... |
@@ -688,7 +702,10 @@ opRegWidth w = pprPanic "opRegWidth" (text "Unsupported width" <+> ppr w) |
|
688
|
702
|
-- Next we compute `c`: The `%not` requires no extension of its operands, but
|
|
689
|
703
|
-- we must still truncate the result back down to 8-bits. Finally the `%shrl`
|
|
690
|
704
|
-- requires no extension and no truncate since we can assume that
|
|
691
|
|
--- `c` is zero-extended (it was produced by a truncateReg in the same block).
|
|
|
705
|
+-- `c` is zero-extended.
|
|
|
706
|
+--
|
|
|
707
|
+-- Down the line I think the right way to approach this is to operate more over
|
|
|
708
|
+-- the `Register` type and store sign extension information inside it.
|
|
692
|
709
|
--
|
|
693
|
710
|
-- TODO:
|
|
694
|
711
|
-- Don't use Width in Operands
|
| ... |
... |
@@ -925,20 +942,36 @@ getRegister' config plat expr |
|
925
|
942
|
getRegister (CmmLoad e (cmmBits w) NaturallyAligned)
|
|
926
|
943
|
|
|
927
|
944
|
CmmMachOp op [e] -> do
|
|
928
|
|
- (reg, _format, code) <- getSomeReg e
|
|
|
945
|
+ register <- getRegister e
|
|
|
946
|
+ (reg, _format, code) <- someReg register
|
|
929
|
947
|
case op of
|
|
930
|
|
- MO_Not w -> return $ Any (intFormat w) $ \dst ->
|
|
|
948
|
+ -- XX Conversion
|
|
|
949
|
+ -- truncateSubwordRegister: See Note [Subword operations on AArch64].
|
|
|
950
|
+ MO_XX_Conv from to
|
|
|
951
|
+ | to >= from -> pure $ swizzleRegisterRep register (intFormat to)
|
|
|
952
|
+ | otherwise -> pure $ truncateSubwordRegister to register
|
|
|
953
|
+
|
|
|
954
|
+ -- truncateSubwordRegister: See Note [Subword operations on AArch64].
|
|
|
955
|
+ MO_Not w -> return $ truncateSubwordRegister w $ Any (intFormat w) $ \dst ->
|
|
931
|
956
|
let w' = opRegWidth w
|
|
932
|
957
|
in code `snocOL`
|
|
933
|
|
- MVN (OpReg w' dst) (OpReg w' reg) `appOL`
|
|
934
|
|
- truncateReg w' w dst -- See Note [Signed arithmetic on AArch64]
|
|
|
958
|
+ MVN (OpReg w' dst) (OpReg w' reg)
|
|
|
959
|
+
|
|
|
960
|
+ -- truncateSubwordRegister: See Note [Subword operations on AArch64].
|
|
|
961
|
+ MO_S_Neg w -> truncateSubwordRegister w <$> do
|
|
|
962
|
+ let op_w = opRegWidth w
|
|
|
963
|
+ (src, _fmt, reg_code) <- someReg $ signExtendRegister w op_w register
|
|
|
964
|
+ pure $ Any (intFormat w) $ \dst -> reg_code `snocOL` (NEG (intFormat w) (OpReg op_w dst) (OpReg op_w src))
|
|
935
|
965
|
|
|
936
|
|
- MO_S_Neg w -> negate code w reg
|
|
937
|
966
|
MO_F_Neg w -> return $ Any fmt (\dst -> code `snocOL` NEG fmt (OpReg w dst) (OpReg w reg))
|
|
938
|
967
|
where fmt = floatFormat w
|
|
939
|
968
|
|
|
940
|
|
- MO_SF_Round from to -> return $ Any (floatFormat to) (\dst -> code `snocOL` SCVTF (OpReg to dst) (OpReg from reg)) -- (Signed ConVerT Float)
|
|
941
|
|
- MO_FS_Truncate from to -> return $ Any (intFormat to) (\dst -> code `snocOL` FCVTZS (OpReg to dst) (OpReg from reg)) -- (float convert (-> zero) signed)
|
|
|
969
|
+ MO_SF_Round from to ->
|
|
|
970
|
+ massert (from >= W32) >>
|
|
|
971
|
+ return $ Any (floatFormat to) (\dst -> code `snocOL` SCVTF (OpReg to dst) (OpReg from reg)) -- (Signed ConVerT Float)
|
|
|
972
|
+ MO_FS_Truncate from to ->
|
|
|
973
|
+ massert (to >= W32) >>
|
|
|
974
|
+ return $ Any (intFormat to) (\dst -> code `snocOL` FCVTZS (OpReg to dst) (OpReg from reg)) -- (float convert (-> zero) signed)
|
|
942
|
975
|
|
|
943
|
976
|
-- TODO this is very hacky
|
|
944
|
977
|
-- Note, UBFM and SBFM expect source and target register to be of the same size, so we'll use @max from to@
|
| ... |
... |
@@ -951,11 +984,8 @@ getRegister' config plat expr |
|
951
|
984
|
MO_FW_Bitcast w -> return $ Any fmt (\dst -> code `snocOL` FMOV fmt (OpReg w dst) (OpReg w reg))
|
|
952
|
985
|
where fmt = intFormat w
|
|
953
|
986
|
|
|
954
|
|
- -- Conversions
|
|
955
|
|
- MO_XX_Conv _from to -> swizzleRegisterRep (intFormat to) <$> getRegister e
|
|
956
|
|
-
|
|
957
|
987
|
-- Vector
|
|
958
|
|
- MO_V_Broadcast l w -> return $ Any fmt (\dst -> code `snocOL` DUP fmt (OpReg vw dst) (OpScalarAsVec w reg))
|
|
|
988
|
+ MO_V_Broadcast l w -> return $ Any fmt (\dst -> code `snocOL` DUP fmt (OpReg vw dst) (OpReg w reg))
|
|
959
|
989
|
where fmt = VecFormat l (intScalarFormat w)
|
|
960
|
990
|
vw = formatToWidth fmt
|
|
961
|
991
|
MO_VF_Broadcast l w -> return $ Any fmt (\dst -> code `snocOL` DUP fmt (OpReg vw dst) (OpScalarAsVec w reg))
|
| ... |
... |
@@ -1054,26 +1084,13 @@ getRegister' config plat expr |
|
1054
|
1084
|
toImm W256 = (OpImm (ImmInt 255))
|
|
1055
|
1085
|
toImm W512 = (OpImm (ImmInt 511))
|
|
1056
|
1086
|
|
|
1057
|
|
- -- In the case of 16- or 8-bit values we need to sign-extend to 32-bits
|
|
1058
|
|
- -- See Note [Signed arithmetic on AArch64].
|
|
1059
|
|
- negate code w reg = do
|
|
1060
|
|
- let w' = opRegWidth w
|
|
1061
|
|
- fmt = intFormat w
|
|
1062
|
|
- (reg', code_sx) <- signExtendReg w w' reg
|
|
1063
|
|
- return $ Any fmt $ \dst ->
|
|
1064
|
|
- code `appOL`
|
|
1065
|
|
- code_sx `snocOL`
|
|
1066
|
|
- NEG fmt (OpReg w' dst) (OpReg w' reg') `appOL`
|
|
1067
|
|
- truncateReg w' w dst
|
|
1068
|
|
-
|
|
1069
|
1087
|
ss_conv from to reg code =
|
|
1070
|
1088
|
let w' = opRegWidth (max from to)
|
|
1071
|
|
- in return $ Any (intFormat to) $ \dst ->
|
|
1072
|
|
- code `snocOL`
|
|
1073
|
|
- SBFM (OpReg w' dst) (OpReg w' reg) (OpImm (ImmInt 0)) (toImm (min from to)) `appOL`
|
|
1074
|
|
- -- At this point an 8- or 16-bit value would be sign-extended
|
|
|
1089
|
+ in return $ truncateSubwordRegister to $ Any (intFormat to) $ \dst ->
|
|
|
1090
|
+ code `snocOL`
|
|
|
1091
|
+ SBFM (OpReg w' dst) (OpReg w' reg) (OpImm (ImmInt 0)) (toImm (min from to))
|
|
|
1092
|
+ -- At this point an 8- or 16-bit value is sign-extended
|
|
1075
|
1093
|
-- to 32-bits. Truncate back down the final width.
|
|
1076
|
|
- truncateReg w' to dst
|
|
1077
|
1094
|
|
|
1078
|
1095
|
-- Dyadic machops:
|
|
1079
|
1096
|
--
|
| ... |
... |
@@ -1090,26 +1107,14 @@ getRegister' config plat expr |
|
1090
|
1107
|
CmmMachOp (MO_Sub _) [expr'@(CmmReg (CmmGlobal _r)), CmmLit (CmmInt 0 _)] -> getRegister' config plat expr'
|
|
1091
|
1108
|
-- Immediates are handled via `getArithImm` in the generic code path.
|
|
1092
|
1109
|
|
|
1093
|
|
- CmmMachOp (MO_U_Quot w) [x, y] | w == W8 -> do
|
|
|
1110
|
+ CmmMachOp (MO_U_Quot w) [x, y] | w == W8 || w == W16-> do
|
|
1094
|
1111
|
(reg_x, _format_x, code_x) <- getSomeReg x
|
|
1095
|
1112
|
(reg_y, _format_y, code_y) <- getSomeReg y
|
|
1096
|
|
- tmp_x <- getNewRegNat (intFormat w)
|
|
1097
|
|
- tmp_y <- getNewRegNat (intFormat w)
|
|
1098
|
|
- return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (UXTB (OpReg w tmp_x) (OpReg w reg_x)) `snocOL`
|
|
1099
|
|
- (UXTB (OpReg w tmp_y) (OpReg w reg_y)) `snocOL`
|
|
1100
|
|
- (UDIV (OpReg w dst) (OpReg w tmp_x) (OpReg w tmp_y)))
|
|
1101
|
|
- CmmMachOp (MO_U_Quot w) [x, y] | w == W16 -> do
|
|
1102
|
|
- (reg_x, _format_x, code_x) <- getSomeReg x
|
|
1103
|
|
- (reg_y, _format_y, code_y) <- getSomeReg y
|
|
1104
|
|
- tmp_x <- getNewRegNat (intFormat w)
|
|
1105
|
|
- tmp_y <- getNewRegNat (intFormat w)
|
|
1106
|
|
- return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (UXTH (OpReg w tmp_x) (OpReg w reg_x)) `snocOL`
|
|
1107
|
|
- (UXTH (OpReg w tmp_y) (OpReg w reg_y)) `snocOL`
|
|
1108
|
|
- (UDIV (OpReg w dst) (OpReg w tmp_x) (OpReg w tmp_y)))
|
|
|
1113
|
+ return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (UDIV (OpReg w dst) (OpReg w reg_x) (OpReg w reg_y)))
|
|
1109
|
1114
|
|
|
1110
|
1115
|
-- 2. Shifts. x << n, x >> n.
|
|
1111
|
1116
|
-- Sub-word left shifts by a constant: use UBFM (UBFIZ alias) to shift
|
|
1112
|
|
- -- and mask in a single instruction. See Note [Signed arithmetic on AArch64].
|
|
|
1117
|
+ -- and mask in a single instruction. See Note [Subword operations on AArch64].
|
|
1113
|
1118
|
CmmMachOp (MO_Shl w) [x, (CmmLit (CmmInt n _))] | w == W8, 0 <= n, n < 8 -> do
|
|
1114
|
1119
|
(reg_x, _format_x, code_x) <- getSomeReg x
|
|
1115
|
1120
|
return $ Any (intFormat w) (\dst -> code_x `snocOL` annExpr expr (UBFM (OpReg w dst) (OpReg w reg_x) (OpImm (ImmInteger ((32 - n) `mod` 32))) (OpImm (ImmInteger (7 - n)))))
|
| ... |
... |
@@ -1126,7 +1131,7 @@ getRegister' config plat expr |
|
1126
|
1131
|
CmmMachOp (MO_S_Shr w) [x, (CmmLit (CmmInt n _))] | w == W8, 0 <= n, n < 8 -> do
|
|
1127
|
1132
|
(reg_x, _format_x, code_x) <- getSomeReg x
|
|
1128
|
1133
|
return $ Any (intFormat w) (\dst -> code_x `snocOL` annExpr expr (SBFX (OpReg w dst) (OpReg w reg_x) (OpImm (ImmInteger n)) (OpImm (ImmInteger (8-n))))
|
|
1129
|
|
- `snocOL` (UXTB (OpReg w dst) (OpReg w dst))) -- See Note [Signed arithmetic on AArch64]
|
|
|
1134
|
+ `snocOL` (UXTB (OpReg w dst) (OpReg w dst))) -- See Note [Subword operations on AArch64]
|
|
1130
|
1135
|
CmmMachOp (MO_S_Shr w) [x, y] | w == W8 -> do
|
|
1131
|
1136
|
(reg_x, _format_x, code_x) <- getSomeReg x
|
|
1132
|
1137
|
(reg_y, _format_y, code_y) <- getSomeReg y
|
| ... |
... |
@@ -1135,12 +1140,12 @@ getRegister' config plat expr |
|
1135
|
1140
|
tmp <- getNewRegNat (intFormat w)
|
|
1136
|
1141
|
return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (SXTB (OpReg w tmp) (OpReg w reg_x)) `snocOL`
|
|
1137
|
1142
|
(ASR (OpReg w dst) (OpReg w tmp) (OpReg w reg_y)) `snocOL`
|
|
1138
|
|
- (UXTB (OpReg w dst) (OpReg w dst))) -- See Note [Signed arithmetic on AArch64]
|
|
|
1143
|
+ (UXTB (OpReg w dst) (OpReg w dst))) -- See Note [Subword operations on AArch64]
|
|
1139
|
1144
|
|
|
1140
|
1145
|
CmmMachOp (MO_S_Shr w) [x, (CmmLit (CmmInt n _))] | w == W16, 0 <= n, n < 16 -> do
|
|
1141
|
1146
|
(reg_x, _format_x, code_x) <- getSomeReg x
|
|
1142
|
1147
|
return $ Any (intFormat w) (\dst -> code_x `snocOL` annExpr expr (SBFX (OpReg w dst) (OpReg w reg_x) (OpImm (ImmInteger n)) (OpImm (ImmInteger (16-n))))
|
|
1143
|
|
- `snocOL` (UXTH (OpReg w dst) (OpReg w dst))) -- See Note [Signed arithmetic on AArch64]
|
|
|
1148
|
+ `snocOL` (UXTH (OpReg w dst) (OpReg w dst))) -- See Note [Subword operations on AArch64]
|
|
1144
|
1149
|
CmmMachOp (MO_S_Shr w) [x, y] | w == W16 -> do
|
|
1145
|
1150
|
(reg_x, _format_x, code_x) <- getSomeReg x
|
|
1146
|
1151
|
(reg_y, _format_y, code_y) <- getSomeReg y
|
| ... |
... |
@@ -1149,7 +1154,7 @@ getRegister' config plat expr |
|
1149
|
1154
|
tmp <- getNewRegNat (intFormat w)
|
|
1150
|
1155
|
return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (SXTH (OpReg w tmp) (OpReg w reg_x)) `snocOL`
|
|
1151
|
1156
|
(ASR (OpReg w dst) (OpReg w tmp) (OpReg w reg_y)) `snocOL`
|
|
1152
|
|
- (UXTH (OpReg w dst) (OpReg w dst))) -- See Note [Signed arithmetic on AArch64]
|
|
|
1157
|
+ (UXTH (OpReg w dst) (OpReg w dst))) -- See Note [Subword operations on AArch64]
|
|
1153
|
1158
|
|
|
1154
|
1159
|
CmmMachOp (MO_S_Shr w) [x, (CmmLit (CmmInt n _))]
|
|
1155
|
1160
|
| w == W32 || w == W64
|
| ... |
... |
@@ -1182,14 +1187,14 @@ getRegister' config plat expr |
|
1182
|
1187
|
return $ Any (intFormat w) (\dst -> code_x `snocOL` annExpr expr (LSR (OpReg w dst) (OpReg w reg_x) (OpImm (ImmInteger n))))
|
|
1183
|
1188
|
|
|
1184
|
1189
|
-- 3. Logic &&, ||
|
|
1185
|
|
- CmmMachOp (MO_And w) [(CmmReg reg), CmmLit (CmmInt n _)] | isAArch64Bitmask (opRegWidth w') (fromIntegral n) ->
|
|
1186
|
|
- return $ Any fmt (\d -> unitOL $ annExpr expr (AND fmt (OpReg w d) (OpReg w' r') (OpImm (ImmInteger n))))
|
|
|
1190
|
+ CmmMachOp (MO_And w) [(CmmReg reg), CmmLit (CmmInt n _)] | Just op_bitmask <- getBitmaskImm n w ->
|
|
|
1191
|
+ return $ Any fmt (\d -> unitOL $ annExpr expr (AND fmt (OpReg w d) (OpReg w' r') op_bitmask))
|
|
1187
|
1192
|
where fmt = intFormat w
|
|
1188
|
1193
|
w' = formatToWidth (cmmTypeFormat (cmmRegType reg))
|
|
1189
|
1194
|
r' = getRegisterReg plat reg
|
|
1190
|
1195
|
|
|
1191
|
|
- CmmMachOp (MO_Or w) [(CmmReg reg), CmmLit (CmmInt n _)] | isAArch64Bitmask (opRegWidth w') (fromIntegral n) ->
|
|
1192
|
|
- return $ Any fmt (\d -> unitOL $ annExpr expr (ORR fmt (OpReg w d) (OpReg w' r') (OpImm (ImmInteger n))))
|
|
|
1196
|
+ CmmMachOp (MO_Or w) [(CmmReg reg), CmmLit (CmmInt n _)] | Just op_bitmask <- getBitmaskImm n w ->
|
|
|
1197
|
+ return $ Any fmt (\d -> unitOL $ annExpr expr (ORR fmt (OpReg w d) (OpReg w' r') op_bitmask))
|
|
1193
|
1198
|
where fmt = intFormat w
|
|
1194
|
1199
|
w' = formatToWidth (cmmTypeFormat (cmmRegType reg))
|
|
1195
|
1200
|
r' = getRegisterReg plat reg
|
| ... |
... |
@@ -1220,16 +1225,17 @@ getRegister' config plat expr |
|
1220
|
1225
|
code_y `appOL`
|
|
1221
|
1226
|
op (OpReg w dst) (OpReg w reg_x) op_y)
|
|
1222
|
1227
|
|
|
1223
|
|
- -- A (potentially signed) integer operation.
|
|
|
1228
|
+ -- A (potentially signed) integer operation that can have immediate arguments.
|
|
1224
|
1229
|
-- In the case of 8- and 16-bit signed arithmetic we must first
|
|
1225
|
1230
|
-- sign-extend both arguments to 32-bits.
|
|
1226
|
|
- -- See Note [Signed arithmetic on AArch64].
|
|
1227
|
|
- intOpImm :: Bool -> Width -> (Operand -> Operand -> Operand -> OrdList Instr) -> (Integer -> Width -> Maybe Operand) -> NatM (Register)
|
|
1228
|
|
- intOpImm {- is signed -} True w op _encode_imm = intOp True w op
|
|
1229
|
|
- intOpImm False w op encode_imm = do
|
|
|
1231
|
+ -- See Note [Subword operations on AArch64].
|
|
|
1232
|
+ intOpImm :: Bool -> SetsHighBits -> Width -> (Operand -> Operand -> Operand -> OrdList Instr) -> (Integer -> Width -> Maybe Operand) -> NatM (Register)
|
|
|
1233
|
+ intOpImm {- is signed -} True trunc w op _encode_imm = intOp True trunc w op
|
|
|
1234
|
+ intOpImm False trunc w op encode_imm = maintainHighBits trunc w <$> do
|
|
1230
|
1235
|
-- compute x<m> <- x
|
|
1231
|
1236
|
-- compute x<o> <- y
|
|
1232
|
1237
|
-- <OP> x<n>, x<m>, x<o>
|
|
|
1238
|
+ let w' = opRegWidth w
|
|
1233
|
1239
|
(reg_x, format_x, code_x) <- getSomeReg x
|
|
1234
|
1240
|
(op_y, format_y, code_y) <- case y of
|
|
1235
|
1241
|
CmmLit (CmmInt n w)
|
| ... |
... |
@@ -1241,40 +1247,29 @@ getRegister' config plat expr |
|
1241
|
1247
|
massertPpr (isIntFormat format_x && isIntFormat format_y) $ text "intOp: non-int"
|
|
1242
|
1248
|
-- This is the width of the registers on which the operation
|
|
1243
|
1249
|
-- should be performed.
|
|
1244
|
|
- let w' = opRegWidth w
|
|
1245
|
1250
|
return $ Any (intFormat w) $ \dst ->
|
|
1246
|
1251
|
code_x `appOL`
|
|
1247
|
1252
|
code_y `appOL`
|
|
1248
|
|
- op (OpReg w' dst) (OpReg w' reg_x) (op_y) `appOL`
|
|
1249
|
|
- truncateReg w' w dst -- truncate back to the operand's original width
|
|
|
1253
|
+ op (OpReg w' dst) (OpReg w' reg_x) (op_y)
|
|
1250
|
1254
|
|
|
1251
|
1255
|
-- A (potentially signed) integer operation.
|
|
1252
|
1256
|
-- In the case of 8- and 16-bit signed arithmetic we must first
|
|
1253
|
1257
|
-- sign-extend both arguments to 32-bits.
|
|
1254
|
|
- -- See Note [Signed arithmetic on AArch64].
|
|
1255
|
|
- intOp is_signed w op = do
|
|
|
1258
|
+ -- See Note [Subword operations on AArch64].
|
|
|
1259
|
+ intOp is_signed clean_highbits w op = maintainHighBits clean_highbits w <$> do
|
|
1256
|
1260
|
-- compute x<m> <- x
|
|
1257
|
1261
|
-- compute x<o> <- y
|
|
1258
|
1262
|
-- <OP> x<n>, x<m>, x<o>
|
|
1259
|
|
- (reg_x, format_x, code_x) <- getSomeReg x
|
|
1260
|
|
- (reg_y, format_y, code_y) <- getSomeReg y
|
|
|
1263
|
+ let op_w = opRegWidth w
|
|
|
1264
|
+ let setHighBits = if is_signed then signExtendRegister w (opRegWidth w) else id
|
|
|
1265
|
+ (reg_x_sx, format_x, code_x) <- someReg =<< setHighBits <$> getRegister x
|
|
|
1266
|
+ (reg_y_sx, format_y, code_y) <- someReg =<< setHighBits <$> getRegister y
|
|
1261
|
1267
|
massertPpr (isIntFormat format_x && isIntFormat format_y) $ text "intOp: non-int"
|
|
1262
|
|
- -- This is the width of the registers on which the operation
|
|
1263
|
|
- -- should be performed.
|
|
1264
|
|
- let w' = opRegWidth w
|
|
1265
|
|
- signExt r
|
|
1266
|
|
- | not is_signed = return (r, nilOL)
|
|
1267
|
|
- | otherwise = signExtendReg w w' r
|
|
1268
|
|
- (reg_x_sx, code_x_sx) <- signExt reg_x
|
|
1269
|
|
- (reg_y_sx, code_y_sx) <- signExt reg_y
|
|
|
1268
|
+
|
|
1270
|
1269
|
return $ Any (intFormat w) $ \dst ->
|
|
1271
|
1270
|
code_x `appOL`
|
|
1272
|
1271
|
code_y `appOL`
|
|
1273
|
|
- -- sign-extend both operands
|
|
1274
|
|
- code_x_sx `appOL`
|
|
1275
|
|
- code_y_sx `appOL`
|
|
1276
|
|
- op (OpReg w' dst) (OpReg w' reg_x_sx) (OpReg w' reg_y_sx) `appOL`
|
|
1277
|
|
- truncateReg w' w dst -- truncate back to the operand's original width
|
|
|
1272
|
+ op (OpReg op_w dst) (OpReg op_w reg_x_sx) (OpReg op_w reg_y_sx)
|
|
1278
|
1273
|
|
|
1279
|
1274
|
floatOp w op = do
|
|
1280
|
1275
|
(reg_fx, format_x, code_fx) <- getFloatReg x
|
| ... |
... |
@@ -1465,9 +1460,9 @@ getRegister' config plat expr |
|
1465
|
1460
|
case op of
|
|
1466
|
1461
|
-- Integer operations
|
|
1467
|
1462
|
-- Add/Sub should only be Integer Options.
|
|
1468
|
|
- MO_Add w -> intOpImm False w (\d x y -> unitOL $ annExpr expr (ADD (intFormat w) d x y)) getArithImm
|
|
|
1463
|
+ MO_Add w -> intOpImm False UnknownHighBits w (\d x y -> unitOL $ annExpr expr (ADD (intFormat w) d x y)) getArithImm
|
|
1469
|
1464
|
-- TODO: Handle sub-word case
|
|
1470
|
|
- MO_Sub w -> intOpImm False w (\d x y -> unitOL $ annExpr expr (SUB (intFormat w) d x y)) getArithImm
|
|
|
1465
|
+ MO_Sub w -> intOpImm False UnknownHighBits w (\d x y -> unitOL $ annExpr expr (SUB (intFormat w) d x y)) getArithImm
|
|
1471
|
1466
|
|
|
1472
|
1467
|
-- Note [CSET]
|
|
1473
|
1468
|
-- ~~~~~~~~~~~
|
| ... |
... |
@@ -1513,9 +1508,9 @@ getRegister' config plat expr |
|
1513
|
1508
|
MO_Ne w -> bitOpImm w (\d x y -> toOL [ CMP x y, CSET d NE ]) getArithImm
|
|
1514
|
1509
|
|
|
1515
|
1510
|
-- Signed multiply/divide
|
|
1516
|
|
- MO_Mul w -> intOp True w (\d x y -> unitOL $ MUL (intFormat w) d x y)
|
|
|
1511
|
+ MO_Mul w -> intOp True UnknownHighBits w (\d x y -> unitOL $ MUL (intFormat w) d x y)
|
|
1517
|
1512
|
MO_S_MulMayOflo w -> do_mul_may_oflo w x y
|
|
1518
|
|
- MO_S_Quot w -> intOp True w (\d x y -> unitOL $ SDIV (intFormat w) d x y)
|
|
|
1513
|
+ MO_S_Quot w -> intOp True UnknownHighBits w (\d x y -> unitOL $ SDIV (intFormat w) d x y)
|
|
1519
|
1514
|
|
|
1520
|
1515
|
-- No native rem instruction. So we'll compute the following
|
|
1521
|
1516
|
-- Rd <- Rx / Ry | 2 <- 7 / 3 -- SDIV Rd Rx Ry
|
| ... |
... |
@@ -1525,24 +1520,24 @@ getRegister' config plat expr |
|
1525
|
1520
|
-- '--------------------------'
|
|
1526
|
1521
|
-- Note the swap in Rx and Ry.
|
|
1527
|
1522
|
MO_S_Rem w -> withTempIntReg w $ \t ->
|
|
1528
|
|
- intOp True w (\d x y -> toOL [ SDIV (intFormat w) t x y, MSUB d t y x ])
|
|
|
1523
|
+ intOp True UnknownHighBits w (\d x y -> toOL [ SDIV (intFormat w) t x y, MSUB d t y x ])
|
|
1529
|
1524
|
|
|
1530
|
1525
|
-- Unsigned multiply/divide
|
|
1531
|
|
- MO_U_Quot w -> intOp False w (\d x y -> unitOL $ UDIV d x y)
|
|
|
1526
|
+ MO_U_Quot w -> intOp False CleanHighBits w (\d x y -> unitOL $ UDIV d x y)
|
|
1532
|
1527
|
MO_U_Rem w -> withTempIntReg w $ \t ->
|
|
1533
|
|
- intOp False w (\d x y -> toOL [ UDIV t x y, MSUB d t y x ])
|
|
|
1528
|
+ intOp False CleanHighBits w (\d x y -> toOL [ UDIV t x y, MSUB d t y x ])
|
|
1534
|
1529
|
|
|
1535
|
1530
|
-- Signed comparisons -- see Note [CSET]
|
|
1536
|
|
- MO_S_Ge w -> intOp True w (\d x y -> toOL [ CMP x y, CSET d SGE ])
|
|
1537
|
|
- MO_S_Le w -> intOp True w (\d x y -> toOL [ CMP x y, CSET d SLE ])
|
|
1538
|
|
- MO_S_Gt w -> intOp True w (\d x y -> toOL [ CMP x y, CSET d SGT ])
|
|
1539
|
|
- MO_S_Lt w -> intOp True w (\d x y -> toOL [ CMP x y, CSET d SLT ])
|
|
|
1531
|
+ MO_S_Ge w -> intOp True CleanHighBits w (\d x y -> toOL [ CMP x y, CSET d SGE ])
|
|
|
1532
|
+ MO_S_Le w -> intOp True CleanHighBits w (\d x y -> toOL [ CMP x y, CSET d SLE ])
|
|
|
1533
|
+ MO_S_Gt w -> intOp True CleanHighBits w (\d x y -> toOL [ CMP x y, CSET d SGT ])
|
|
|
1534
|
+ MO_S_Lt w -> intOp True CleanHighBits w (\d x y -> toOL [ CMP x y, CSET d SLT ])
|
|
1540
|
1535
|
|
|
1541
|
1536
|
-- Unsigned comparisons
|
|
1542
|
|
- MO_U_Ge w -> intOpImm False w (\d x y -> toOL [ CMP x y, CSET d UGE ]) getArithImm
|
|
1543
|
|
- MO_U_Le w -> intOpImm False w (\d x y -> toOL [ CMP x y, CSET d ULE ]) getArithImm
|
|
1544
|
|
- MO_U_Gt w -> intOpImm False w (\d x y -> toOL [ CMP x y, CSET d UGT ]) getArithImm
|
|
1545
|
|
- MO_U_Lt w -> intOpImm False w (\d x y -> toOL [ CMP x y, CSET d ULT ]) getArithImm
|
|
|
1537
|
+ MO_U_Ge w -> intOpImm False CleanHighBits w (\d x y -> toOL [ CMP x y, CSET d UGE ]) getArithImm
|
|
|
1538
|
+ MO_U_Le w -> intOpImm False CleanHighBits w (\d x y -> toOL [ CMP x y, CSET d ULE ]) getArithImm
|
|
|
1539
|
+ MO_U_Gt w -> intOpImm False CleanHighBits w (\d x y -> toOL [ CMP x y, CSET d UGT ]) getArithImm
|
|
|
1540
|
+ MO_U_Lt w -> intOpImm False CleanHighBits w (\d x y -> toOL [ CMP x y, CSET d ULT ]) getArithImm
|
|
1546
|
1541
|
|
|
1547
|
1542
|
-- Floating point arithmetic
|
|
1548
|
1543
|
MO_F_Add w -> floatOp w (\d x y -> unitOL $ ADD (floatFormat w) d x y)
|
| ... |
... |
@@ -1570,9 +1565,9 @@ getRegister' config plat expr |
|
1570
|
1565
|
MO_And w -> bitOpImm w (\d x y -> unitOL $ AND (intFormat w) d x y) getBitmaskImm
|
|
1571
|
1566
|
MO_Or w -> bitOpImm w (\d x y -> unitOL $ ORR (intFormat w) d x y) getBitmaskImm
|
|
1572
|
1567
|
MO_Xor w -> bitOpImm w (\d x y -> unitOL $ EOR (intFormat w) d x y) getBitmaskImm
|
|
1573
|
|
- MO_Shl w -> intOp False w (\d x y -> unitOL $ LSL d x y)
|
|
1574
|
|
- MO_U_Shr w -> intOp False w (\d x y -> unitOL $ LSR d x y)
|
|
1575
|
|
- MO_S_Shr w -> intOp True w (\d x y -> unitOL $ ASR d x y)
|
|
|
1568
|
+ MO_Shl w -> intOp False UnknownHighBits w (\d x y -> unitOL $ LSL d x y)
|
|
|
1569
|
+ MO_U_Shr w -> intOp False CleanHighBits w (\d x y -> unitOL $ LSR d x y)
|
|
|
1570
|
+ MO_S_Shr w -> intOp True UnknownHighBits w (\d x y -> unitOL $ ASR d x y)
|
|
1576
|
1571
|
|
|
1577
|
1572
|
-- Vector operations
|
|
1578
|
1573
|
MO_V_Add l w -> intVecOp l w (\fmt d x y -> unitOL $ ADD fmt d x y)
|
| ... |
... |
@@ -1630,7 +1625,7 @@ getRegister' config plat expr |
|
1630
|
1625
|
_ -> pprPanic "Unsupported offset" (pdoc platform y)
|
|
1631
|
1626
|
(reg_x, format_x, code_x) <- getSomeReg x
|
|
1632
|
1627
|
massertPpr (isVecFormat format_x) $ text "MO_V_Extract: non-vector"
|
|
1633
|
|
- -- Always use UMOV. See Note [Signed arithmetic on AArch64]
|
|
|
1628
|
+ -- Always use UMOV. See Note [Subword operations on AArch64]
|
|
1634
|
1629
|
return $ Any format (\dst -> code_x `snocOL` UMOV (OpReg w dst) (OpVecLane w reg_x index))
|
|
1635
|
1630
|
|
|
1636
|
1631
|
MO_VF_Extract l w -> do
|
| ... |
... |
@@ -1759,7 +1754,7 @@ getRegister' config plat expr |
|
1759
|
1754
|
tmp <- getNewRegNat format
|
|
1760
|
1755
|
return $ Any format $ \dst ->
|
|
1761
|
1756
|
code_x `appOL` code_y `appOL`
|
|
1762
|
|
- if dst == reg_y
|
|
|
1757
|
+ if dst == reg_y --unlike MO_V_Insert here y/dst can overlap.
|
|
1763
|
1758
|
then toOL [ MOV (OpReg W128 tmp) (OpReg W128 reg_x)
|
|
1764
|
1759
|
, INS format (OpVecLane w tmp index) (OpScalarAsVec w reg_y)
|
|
1765
|
1760
|
, MOV (OpReg W128 dst) (OpReg W128 tmp)
|
| ... |
... |
@@ -1886,36 +1881,87 @@ isAArch64Bitmask width n = |
|
1886
|
1881
|
hasOneRun m =
|
|
1887
|
1882
|
64 == popCount m + countLeadingZeros m + countTrailingZeros m
|
|
1888
|
1883
|
|
|
|
1884
|
+--------------------------------------------------------------------------------
|
|
|
1885
|
+-- Helpers to help enforcing Note [Subword operations on AArch64]
|
|
|
1886
|
+--------------------------------------------------------------------------------
|
|
|
1887
|
+
|
|
1889
|
1888
|
-- | Instructions to sign-extend the value in the given register from width @w@
|
|
1890
|
1889
|
-- up to width @w'@.
|
|
1891
|
|
-signExtendReg :: Width -> Width -> Reg -> NatM (Reg, OrdList Instr)
|
|
1892
|
|
-signExtendReg w w' r =
|
|
1893
|
|
- case w of
|
|
1894
|
|
- W64 -> noop
|
|
1895
|
|
- W32
|
|
1896
|
|
- | w' == W32 -> noop
|
|
1897
|
|
- | otherwise -> extend SXTW
|
|
1898
|
|
- W16 -> extend SXTH
|
|
1899
|
|
- W8 -> extend SXTB
|
|
1900
|
|
- _ -> panic "intOp"
|
|
|
1890
|
+signExtendInstr :: Width -> Width -> Reg -> Maybe (Reg -> Instr)
|
|
|
1891
|
+signExtendInstr w w' r =
|
|
|
1892
|
+ case (w,w') of
|
|
|
1893
|
+ (W64,_) -> Nothing
|
|
|
1894
|
+ (W32,W32) -> Nothing
|
|
|
1895
|
+ (W32,_) -> extend SXTW
|
|
|
1896
|
+ (W16,_) -> extend SXTH
|
|
|
1897
|
+ (W8 ,_) -> extend SXTB
|
|
|
1898
|
+ _ -> panic "signExtendInstr:unexpectedWidth"
|
|
|
1899
|
+ where
|
|
|
1900
|
+ extend instr = Just $ \r' -> instr (OpReg w' r') (OpReg w r)
|
|
|
1901
|
+
|
|
|
1902
|
+-- | Sign extend the register if needed, otherwise use register as-is
|
|
|
1903
|
+signExtendRegister :: Width -> Width -> Register -> Register
|
|
|
1904
|
+signExtendRegister w w' register = case register of
|
|
|
1905
|
+ Fixed _fmt reg code ->
|
|
|
1906
|
+ maybe register
|
|
|
1907
|
+ (\instr_ext -> Any (intFormat w') (\dst -> code `snocOL` instr_ext dst) )
|
|
|
1908
|
+ (signExtendInstr w w' reg)
|
|
|
1909
|
+ Any _fmt code ->
|
|
|
1910
|
+ Any (intFormat w') $ \dst ->
|
|
|
1911
|
+ maybe (code dst)
|
|
|
1912
|
+ (\instr_ext -> code dst `snocOL` instr_ext dst)
|
|
|
1913
|
+ (signExtendInstr w w' dst)
|
|
|
1914
|
+
|
|
|
1915
|
+truncSubwordRegInstr :: Width -> Reg -> Maybe (Reg -> Instr)
|
|
|
1916
|
+truncSubwordRegInstr w_to r =
|
|
|
1917
|
+ case w_to of
|
|
|
1918
|
+ -- Asserted false, but be defensive for non-debug builds.
|
|
|
1919
|
+ W64 -> Nothing
|
|
|
1920
|
+ W32 -> Nothing
|
|
|
1921
|
+
|
|
|
1922
|
+ -- Actual truncation
|
|
|
1923
|
+ W16 -> trunc W32 UXTH
|
|
|
1924
|
+ W8 -> trunc W32 UXTB
|
|
|
1925
|
+ _ -> panic "truncateSubwordReg:unexpectedWidth"
|
|
1901
|
1926
|
where
|
|
1902
|
|
- noop = return (r, nilOL)
|
|
1903
|
|
- extend instr = do
|
|
1904
|
|
- r' <- getNewRegNat (intFormat w')
|
|
1905
|
|
- return (r', unitOL $ instr (OpReg w' r') (OpReg w r))
|
|
1906
|
|
-
|
|
1907
|
|
--- | Instructions to truncate the value in the given register from width @w@
|
|
1908
|
|
--- down to width @w'@.
|
|
1909
|
|
-truncateReg :: Width -> Width -> Reg -> OrdList Instr
|
|
1910
|
|
-truncateReg w w' r =
|
|
1911
|
|
- case w of
|
|
|
1927
|
+ trunc w instr = do
|
|
|
1928
|
+ Just $ \r' -> instr (OpReg w r') (OpReg w r)
|
|
|
1929
|
+
|
|
|
1930
|
+-- | Like @truncateSubwordRegister@, but modifes the given argument register in place if we
|
|
|
1931
|
+-- need to truncate.
|
|
|
1932
|
+truncateSubwordRegInplace :: Width -> Reg -> OrdList Instr
|
|
|
1933
|
+truncateSubwordRegInplace w_to r = do
|
|
|
1934
|
+ case w_to of
|
|
1912
|
1935
|
W64 -> nilOL
|
|
1913
|
|
- W32
|
|
1914
|
|
- | w' == W32 -> nilOL
|
|
1915
|
|
- _ -> unitOL $ UBFM (OpReg w r)
|
|
1916
|
|
- (OpReg w r)
|
|
1917
|
|
- (OpImm (ImmInt 0))
|
|
1918
|
|
- (OpImm $ ImmInt $ widthInBits w' - 1)
|
|
|
1936
|
+ W32 -> nilOL
|
|
|
1937
|
+ W16 -> trunc UXTH
|
|
|
1938
|
+ W8 -> trunc UXTB
|
|
|
1939
|
+ _ -> panic "truncateSubwordRegInplace:unexpectedWidth"
|
|
|
1940
|
+ where
|
|
|
1941
|
+ trunc instr = do
|
|
|
1942
|
+ unitOL $ instr (OpReg W32 r) (OpReg W32 r)
|
|
|
1943
|
+
|
|
|
1944
|
+-- | Zeros the high words of the value represented by Register if needed according to
|
|
|
1945
|
+-- Note [Subword operations on AArch64]
|
|
|
1946
|
+truncateSubwordRegister :: Width -> Register -> Register
|
|
|
1947
|
+truncateSubwordRegister w register = case register of
|
|
|
1948
|
+ Fixed _fmt reg code ->
|
|
|
1949
|
+ maybe (swizzleRegisterRep register (intFormat w))
|
|
|
1950
|
+ (\r_instr -> Any (intFormat w) (\dst -> code `snocOL` r_instr dst))
|
|
|
1951
|
+ (truncSubwordRegInstr w reg)
|
|
|
1952
|
+ Any _fmt code -> Any (intFormat w) $ \dst ->
|
|
|
1953
|
+ maybe (code dst) (\r_inst -> code dst `snocOL` r_inst dst) (truncSubwordRegInstr w dst)
|
|
|
1954
|
+
|
|
|
1955
|
+data SetsHighBits = UnknownHighBits | CleanHighBits
|
|
|
1956
|
+
|
|
|
1957
|
+maintainHighBits :: SetsHighBits -> Width -> Register -> Register
|
|
|
1958
|
+maintainHighBits CleanHighBits _w x = x
|
|
|
1959
|
+maintainHighBits UnknownHighBits w x = truncateSubwordRegister w x
|
|
|
1960
|
+
|
|
|
1961
|
+-- Reinterpret the value in the register as different format.
|
|
|
1962
|
+swizzleRegisterRep :: Register -> Format -> Register
|
|
|
1963
|
+swizzleRegisterRep (Fixed _ reg code) format = Fixed format reg code
|
|
|
1964
|
+swizzleRegisterRep (Any _ codefn) format = Any format codefn
|
|
1919
|
1965
|
|
|
1920
|
1966
|
-- -----------------------------------------------------------------------------
|
|
1921
|
1967
|
-- The 'Amode' type: Memory addressing modes passed up the tree.
|
| ... |
... |
@@ -2038,27 +2084,24 @@ genCondJump bid expr = do |
|
2038
|
2084
|
-- Generic case.
|
|
2039
|
2085
|
CmmMachOp mop [x, y] -> do
|
|
2040
|
2086
|
|
|
2041
|
|
- let ubcond w cmp = do
|
|
2042
|
|
- -- compute both sides.
|
|
2043
|
|
- (reg_x, _format_x, code_x) <- getSomeReg x
|
|
2044
|
|
- (reg_y, _format_y, code_y) <- getSomeReg y
|
|
2045
|
|
- let x' = OpReg w reg_x
|
|
2046
|
|
- y' = OpReg w reg_y
|
|
2047
|
|
- return $ case w of
|
|
2048
|
|
- W8 -> code_x `appOL` code_y `appOL` toOL [ UXTB x' x', UXTB y' y', CMP x' y', (annExpr expr (BCOND cmp (TBlock bid))) ]
|
|
2049
|
|
- W16 -> code_x `appOL` code_y `appOL` toOL [ UXTH x' x', UXTH y' y', CMP x' y', (annExpr expr (BCOND cmp (TBlock bid))) ]
|
|
2050
|
|
- _ -> code_x `appOL` code_y `appOL` toOL [ CMP x' y', (annExpr expr (BCOND cmp (TBlock bid))) ]
|
|
2051
|
|
-
|
|
2052
|
|
- sbcond w cmp = do
|
|
2053
|
|
- -- compute both sides.
|
|
2054
|
|
- (reg_x, _format_x, code_x) <- getSomeReg x
|
|
2055
|
|
- (reg_y, _format_y, code_y) <- getSomeReg y
|
|
|
2087
|
+ let icond is_signed w cmp = do
|
|
|
2088
|
+ -- zero or sign extend the argument register(s)
|
|
|
2089
|
+ let extend reg =
|
|
|
2090
|
+ if is_signed
|
|
|
2091
|
+ then someReg $ signExtendRegister w (opRegWidth w) reg
|
|
|
2092
|
+ else someReg reg
|
|
|
2093
|
+
|
|
|
2094
|
+ (reg_x, _format_x, code_x) <- extend =<< getRegister x
|
|
|
2095
|
+ (reg_y, _format_y, code_y) <- extend =<< getRegister y
|
|
|
2096
|
+
|
|
2056
|
2097
|
let x' = OpReg w reg_x
|
|
2057
|
2098
|
y' = OpReg w reg_y
|
|
2058
|
|
- return $ case w of
|
|
2059
|
|
- W8 -> code_x `appOL` code_y `appOL` toOL [ SXTB x' x', SXTB y' y', CMP x' y', (annExpr expr (BCOND cmp (TBlock bid))) ]
|
|
2060
|
|
- W16 -> code_x `appOL` code_y `appOL` toOL [ SXTH x' x', SXTH y' y', CMP x' y', (annExpr expr (BCOND cmp (TBlock bid))) ]
|
|
2061
|
|
- _ -> code_x `appOL` code_y `appOL` toOL [ CMP x' y', (annExpr expr (BCOND cmp (TBlock bid))) ]
|
|
|
2099
|
+
|
|
|
2100
|
+ return $ concatOL [code_x, code_y,
|
|
|
2101
|
+ toOL [CMP x' y', (annExpr expr (BCOND cmp (TBlock bid)))]]
|
|
|
2102
|
+
|
|
|
2103
|
+ let ubcond w cmp = icond False w cmp
|
|
|
2104
|
+ sbcond w cmp = icond True w cmp
|
|
2062
|
2105
|
|
|
2063
|
2106
|
fbcond w cmp = do
|
|
2064
|
2107
|
-- ensure we get float regs
|
| ... |
... |
@@ -2327,32 +2370,27 @@ genCCall target dest_regs arg_regs = do |
|
2327
|
2370
|
, [src_a, src_b] <- arg_regs
|
|
2328
|
2371
|
, [dst_needed, dst_hi, dst_lo] <- dest_regs
|
|
2329
|
2372
|
-> do
|
|
2330
|
|
- (reg_a', _format_x, code_a) <- getSomeReg src_a
|
|
2331
|
|
- (reg_b', _format_y, code_b) <- getSomeReg src_b
|
|
|
2373
|
+ -- Sign-extend inputs to W32 for SMULL (Xd = Wn * Wm).
|
|
|
2374
|
+ -- sign extension always allocates a fresh temp for w < W32,
|
|
|
2375
|
+ -- and is a noop for W32 (safe: SMULL reads both sources
|
|
|
2376
|
+ -- atomically before writing the destination).
|
|
|
2377
|
+ (reg_a, _format_x, code_a) <- someReg =<< signExtendRegister w W32 <$> getRegister src_a
|
|
|
2378
|
+ (reg_b, _format_y, code_b) <- someReg =<< signExtendRegister w W32 <$> getRegister src_b
|
|
2332
|
2379
|
|
|
2333
|
2380
|
let lo = getRegisterReg platform (CmmLocal dst_lo)
|
|
2334
|
2381
|
hi = getRegisterReg platform (CmmLocal dst_hi)
|
|
2335
|
2382
|
nd = getRegisterReg platform (CmmLocal dst_needed)
|
|
2336
|
2383
|
w' = platformWordWidth platform
|
|
2337
|
2384
|
|
|
2338
|
|
- -- Sign-extend inputs to W32 for SMULL (Xd = Wn * Wm).
|
|
2339
|
|
- -- signExtendReg always allocates a fresh temp for w < W32,
|
|
2340
|
|
- -- and is a noop for W32 (safe: SMULL reads both sources
|
|
2341
|
|
- -- atomically before writing the destination).
|
|
2342
|
|
- (reg_a, code_a') <- signExtendReg w W32 reg_a'
|
|
2343
|
|
- (reg_b, code_b') <- signExtendReg w W32 reg_b'
|
|
2344
|
|
-
|
|
2345
|
2385
|
return $
|
|
2346
|
2386
|
code_a `appOL`
|
|
2347
|
|
- code_b `appOL`
|
|
2348
|
|
- code_a' `appOL`
|
|
2349
|
|
- code_b' `snocOL`
|
|
|
2387
|
+ code_b `snocOL`
|
|
2350
|
2388
|
-- SMULL Xd, Wn, Wm: multiply two W32 values producing a
|
|
2351
|
2389
|
-- 64-bit result. The low w bits of lo contain the truncated
|
|
2352
|
2390
|
-- product, and hi gets the overflow (sign extension bits).
|
|
2353
|
2391
|
SMULL (OpReg w' lo) (OpReg W32 reg_a) (OpReg W32 reg_b) `snocOL`
|
|
2354
|
2392
|
ASR (OpReg w' hi) (OpReg w' lo) (OpImm (ImmInt $ widthInBits w)) `appOL`
|
|
2355
|
|
- truncateReg w' w lo `snocOL`
|
|
|
2393
|
+ truncateSubwordRegInplace w lo `snocOL`
|
|
2356
|
2394
|
-- CMN (compare negative) tests hi + lo' == 0, i.e. hi == -lo'.
|
|
2357
|
2395
|
-- lo' = LSR(lo, w-1) gives 1 if lo is negative, 0 if positive.
|
|
2358
|
2396
|
-- No overflow iff hi is the sign extension of lo:
|
| ... |
... |
@@ -2362,7 +2400,7 @@ genCCall target dest_regs arg_regs = do |
|
2362
|
2400
|
-- NE to set nd = 1 when overflow occurred.
|
|
2363
|
2401
|
CMN (OpReg w' hi) (OpRegShift w' lo SLSR (widthInBits w - 1)) `snocOL`
|
|
2364
|
2402
|
CSET (OpReg w' nd) NE `appOL`
|
|
2365
|
|
- truncateReg w' w hi
|
|
|
2403
|
+ truncateSubwordRegInplace w hi
|
|
2366
|
2404
|
-- Can't handle > 64 bit operands
|
|
2367
|
2405
|
| otherwise -> unsupported (MO_S_Mul2 w)
|
|
2368
|
2406
|
PrimTarget (MO_U_Mul2 w)
|
| ... |
... |
@@ -2385,7 +2423,7 @@ genCCall target dest_regs arg_regs = do |
|
2385
|
2423
|
)
|
|
2386
|
2424
|
-- For sizes < platform width, we can just perform a multiply and shift
|
|
2387
|
2425
|
-- Need to be careful to truncate the low half, but the upper half should be
|
|
2388
|
|
- -- be ok if the invariant in [Signed arithmetic on AArch64] is maintained.
|
|
|
2426
|
+ -- be ok if the invariant in Note [Subword operations on AArch64] is maintained.
|
|
2389
|
2427
|
-- Currently this case can't be produced by the compiler since
|
|
2390
|
2428
|
-- timesWord2# :: Word# -> Word# -> (# Word#, Word# #)
|
|
2391
|
2429
|
-- TODO: Remove? Or would the extra primop be useful for avoiding the extra
|
| ... |
... |
@@ -2412,7 +2450,7 @@ genCCall target dest_regs arg_regs = do |
|
2412
|
2450
|
(OpImm (ImmInt $ widthInBits w)) -- lsb
|
|
2413
|
2451
|
(OpImm (ImmInt $ widthInBits w)) -- width to extract
|
|
2414
|
2452
|
`appOL`
|
|
2415
|
|
- truncateReg W64 w lo
|
|
|
2453
|
+ truncateSubwordRegInplace w lo
|
|
2416
|
2454
|
)
|
|
2417
|
2455
|
| otherwise -> unsupported (MO_U_Mul2 w)
|
|
2418
|
2456
|
PrimTarget (MO_Clz w)
|
| ... |
... |
@@ -2730,6 +2768,7 @@ genCCall target dest_regs arg_regs = do |
|
2730
|
2768
|
| [p_reg, val_reg] <- arg_regs -> do
|
|
2731
|
2769
|
(p, _fmt_p, code_p) <- getSomeReg p_reg
|
|
2732
|
2770
|
(val, fmt_val, code_val) <- getSomeReg val_reg
|
|
|
2771
|
+ massert (fmt_val == intFormat w)
|
|
2733
|
2772
|
let instr = case ord of
|
|
2734
|
2773
|
MemOrderRelaxed -> STR
|
|
2735
|
2774
|
_ -> STLR
|
| ... |
... |
@@ -2845,6 +2884,7 @@ genCCall target dest_regs arg_regs = do |
|
2845
|
2884
|
W16 -> SXTH (OpReg W64 gpReg) (OpReg w r)
|
|
2846
|
2885
|
_ -> panic "impossible"
|
|
2847
|
2886
|
| otherwise
|
|
|
2887
|
+ -- Relies on Note [Subword operations on AArch64]
|
|
2848
|
2888
|
= MOV (OpReg w gpReg) (OpReg w r)
|
|
2849
|
2889
|
accumCode' = accumCode `appOL`
|
|
2850
|
2890
|
code_r `snocOL`
|
| ... |
... |
@@ -2898,6 +2938,7 @@ genCCall target dest_regs arg_regs = do |
|
2898
|
2938
|
|
|
2899
|
2939
|
passArguments _ _ _ _ _ _ _ = pprPanic "passArguments" (text "invalid state")
|
|
2900
|
2940
|
|
|
|
2941
|
+ -- readResults gpArgs fpArgs dest_regs reg_acc code_acc
|
|
2901
|
2942
|
readResults :: [Reg] -> [Reg] -> [LocalReg] -> [Reg]-> InstrBlock -> NatM (InstrBlock)
|
|
2902
|
2943
|
readResults _ _ [] _ accumCode = return accumCode
|
|
2903
|
2944
|
readResults [] _ _ _ _ = do
|
| ... |
... |
@@ -2915,7 +2956,14 @@ genCCall target dest_regs arg_regs = do |
|
2915
|
2956
|
r_dst = getRegisterReg platform (CmmLocal dst)
|
|
2916
|
2957
|
if isFloatFormat format || isVecFormat format
|
|
2917
|
2958
|
then readResults (gpReg:gpRegs) fpRegs dsts (fpReg:accumRegs) (accumCode `snocOL` MOV (OpReg w r_dst) (OpReg w fpReg))
|
|
2918
|
|
- else readResults gpRegs (fpReg:fpRegs) dsts (gpReg:accumRegs) (accumCode `snocOL` MOV (OpReg w r_dst) (OpReg w gpReg))
|
|
|
2959
|
+ else do
|
|
|
2960
|
+ -- Needed, ffi calls can return garbage in high bits.
|
|
|
2961
|
+ -- See Note [Subword operations on AArch64]
|
|
|
2962
|
+ let !mov_instr = case w of
|
|
|
2963
|
+ W8 -> UXTB
|
|
|
2964
|
+ W16 -> UXTH
|
|
|
2965
|
+ _ -> MOV
|
|
|
2966
|
+ readResults gpRegs (fpReg:fpRegs) dsts (gpReg:accumRegs) (accumCode `snocOL` mov_instr (OpReg w r_dst) (OpReg w gpReg))
|
|
2919
|
2967
|
|
|
2920
|
2968
|
unaryFloatOp w op arg_reg dest_reg = do
|
|
2921
|
2969
|
platform <- getPlatform
|