GHC issueshttps://gitlab.haskell.org/ghc/ghc/-/issues2020-06-04T18:29:12Zhttps://gitlab.haskell.org/ghc/ghc/-/issues/14251LLVM Code Gen messes up registers2020-06-04T18:29:12ZMoritz AngermannLLVM Code Gen messes up registersDue to the way the LLVM Code Gen generates Function Singnatures, it is possible to end up mixed up registers.
A slightly adapted T8064
```hs
{-# LANGUAGE MagicHash, BangPatterns #-}
module Main where
import GHC.Exts
{-# NOINLINE f #-...Due to the way the LLVM Code Gen generates Function Singnatures, it is possible to end up mixed up registers.
A slightly adapted T8064
```hs
{-# LANGUAGE MagicHash, BangPatterns #-}
module Main where
import GHC.Exts
{-# NOINLINE f #-}
f :: (Int# -> Float# -> Double# -> Float# -> Double# -> String) -> String
f g = g 3# 4.0# 5.0## 6.0# 6.9## ++ " World!"
{-# NOINLINE p #-}
p :: Int# -> Float# -> Double# -> Float# -> Double# -> String
p i j k l m = "Hello"
{-# NOINLINE q #-}
q :: Int# -> Int# -> Float# -> Double# -> Float# -> Double# -> String
q _ i j k l m = "Hello " ++ show (F# l) ++ " " ++ show (D# m)
{-# NOINLINE r #-}
r :: Int# -> Float# -> Double# -> Float# -> Double# -> String
r i = let !(I# z) = length [I# 1# .. I# i] in \j k l m -> p z j k l m
-- ghc won't eta-expand around the length, because it has unknown cost
main = do
putStrLn (f p) -- fast call
putStrLn (f r) -- slow call: function but wrong arity
let g = last [q 1#]
putStrLn (f g) -- slow call: thunk
```
will produce the following results:
```
../inplace/bin/ghc-stage1 -fllvm -fforce-recomp T6084.hs -O2 -o T6084-llvm && ./T6084-llvm
[1 of 1] Compiling Main ( T6084.hs, T6084.o )
Linking T6084-llvm ...
Hello World!
Hello World!
Hello 4.0 5.0 World!
../inplace/bin/ghc-stage1 -fasm -fforce-recomp T6084.hs -O2 -o T6084-asm && ./T6084-asm
[1 of 1] Compiling Main ( T6084.hs, T6084.o )
Linking T6084-asm ...
Hello World!
Hello World!
Hello 6.0 6.9 World!
```
The underlying reason is that (at least for X86_64) the Float and Double registers alternate.
The llvm code gen creates function signatures based on the live registers (instead of all).
For `q` only the last Float and Double register are `live`. However when calling `q` we pass
`f1: Float -> d1: Double -> f2: Float -> d2: Double`. `f2` and `d2` are silently ignored, and in
the function body, we now have `f2 <- f1` and `d2 <- d1`.
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ------------------------- |
| Version | 8.3 |
| Type | Bug |
| TypeOfFailure | OtherFailure |
| Priority | highest |
| Resolution | Unresolved |
| Component | Compiler (LLVM) |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | bgamari, carter, simonmar |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"LLVM Code Gen messes up registers","status":"New","operating_system":"","component":"Compiler (LLVM)","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"8.3","keywords":[],"differentials":[],"test_case":"","architecture":"","cc":["bgamari","carter","simonmar"],"type":"Bug","description":"Due to the way the LLVM Code Gen generates Function Singnatures, it is possible to end up mixed up registers.\r\n\r\nA slightly adapted T8064\r\n{{{#!hs\r\n{-# LANGUAGE MagicHash, BangPatterns #-}\r\nmodule Main where\r\n\r\nimport GHC.Exts\r\n\r\n{-# NOINLINE f #-}\r\nf :: (Int# -> Float# -> Double# -> Float# -> Double# -> String) -> String\r\nf g = g 3# 4.0# 5.0## 6.0# 6.9## ++ \" World!\"\r\n\r\n{-# NOINLINE p #-}\r\np :: Int# -> Float# -> Double# -> Float# -> Double# -> String\r\np i j k l m = \"Hello\"\r\n\r\n{-# NOINLINE q #-}\r\nq :: Int# -> Int# -> Float# -> Double# -> Float# -> Double# -> String\r\nq _ i j k l m = \"Hello \" ++ show (F# l) ++ \" \" ++ show (D# m)\r\n\r\n{-# NOINLINE r #-}\r\nr :: Int# -> Float# -> Double# -> Float# -> Double# -> String\r\nr i = let !(I# z) = length [I# 1# .. I# i] in \\j k l m -> p z j k l m\r\n -- ghc won't eta-expand around the length, because it has unknown cost\r\n\r\nmain = do\r\n putStrLn (f p) -- fast call\r\n putStrLn (f r) -- slow call: function but wrong arity\r\n let g = last [q 1#]\r\n putStrLn (f g) -- slow call: thunk\r\n}}}\r\n\r\nwill produce the following results:\r\n\r\n{{{\r\n../inplace/bin/ghc-stage1 -fllvm -fforce-recomp T6084.hs -O2 -o T6084-llvm && ./T6084-llvm\r\n[1 of 1] Compiling Main ( T6084.hs, T6084.o )\r\nLinking T6084-llvm ...\r\nHello World!\r\nHello World!\r\nHello 4.0 5.0 World!\r\n\r\n../inplace/bin/ghc-stage1 -fasm -fforce-recomp T6084.hs -O2 -o T6084-asm && ./T6084-asm\r\n[1 of 1] Compiling Main ( T6084.hs, T6084.o )\r\nLinking T6084-asm ...\r\nHello World!\r\nHello World!\r\nHello 6.0 6.9 World!\r\n}}}\r\n\r\nThe underlying reason is that (at least for X86_64) the Float and Double registers alternate.\r\nThe llvm code gen creates function signatures based on the live registers (instead of all).\r\n\r\nFor `q` only the last Float and Double register are `live`. However when calling `q` we pass\r\n`f1: Float -> d1: Double -> f2: Float -> d2: Double`. `f2` and `d2` are silently ignored, and in\r\nthe function body, we now have `f2 <- f1` and `d2 <- d1`.","type_of_failure":"OtherFailure","blocking":[]} -->8.10.1Kavon FarvardinKavon Farvardinhttps://gitlab.haskell.org/ghc/ghc/-/issues/13904LLVM does not need to trash caller-saved registers.2020-01-20T23:41:13ZKavon FarvardinLLVM does not need to trash caller-saved registers.- \*Summary\*\*: This ticket is a simplification of the LLVM backend that I already plan to do in another branch, but I'd like to push the patch into `master`, and then merge from `master` into that branch.
The LLVM backend attempts to...- \*Summary\*\*: This ticket is a simplification of the LLVM backend that I already plan to do in another branch, but I'd like to push the patch into `master`, and then merge from `master` into that branch.
The LLVM backend attempts to trash the physical registers corresponding to Rx, Fx, and Dx registers that are caller-saved before an FFI call by emitting a sequence such as this:
```llvm
store i64 undef, i64* %R3_Var
store i64 undef, i64* %R4_Var
store i64 undef, i64* %R5_Var
store i64 undef, i64* %R6_Var
store float undef, float* %F1_Var
store double undef, double* %D1_Var
store float undef, float* %F2_Var
store double undef, double* %D2_Var
store float undef, float* %F3_Var
store double undef, double* %D3_Var
store float undef, float* %F4_Var
store double undef, double* %D4_Var
store float undef, float* %F5_Var
store double undef, double* %D5_Var
store float undef, float* %F6_Var
store double undef, double* %D6_Var
%ln7bu = call ccc double (double) @llvm.sin.f64( double %ln7bs ) nounwind
```
Where the Rx/Fx/Dx registers are chosen based on what physical register those global registers map to, and whether those physical registers are caller-saved according to the C ABI.
I'm certain this is unnecessary, as there is no way to know the physical register corresponding to those vars within an LLVM IR function. The calling convention used for the FFI call will preserve caller-saved registers as needed, i.e., only if such a value is needed after the call.
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ---------------------- |
| Version | 8.0.1 |
| Type | Bug |
| TypeOfFailure | CompileTimePerformance |
| Priority | normal |
| Resolution | Unresolved |
| Component | Compiler |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | #4992 |
| Blocking | |
| CC | |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"LLVM does not need to trash callee-saved registers.","status":"New","operating_system":"","component":"Compiler","related":[4992],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"8.0.1","keywords":["codegen","llvm,"],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"Bug","description":"**Summary**: This ticket is a simplification of the LLVM backend that I already plan to do in another branch, but I'd like to push the patch into `master`, and then merge from `master` into that branch. \r\n\r\nThe LLVM backend attempts to trash the physical registers corresponding to Rx, Fx, and Dx registers that are caller-saved before an FFI call by emitting a sequence such as this:\r\n\r\n{{{#!llvm\r\n store i64 undef, i64* %R3_Var\r\n store i64 undef, i64* %R4_Var\r\n store i64 undef, i64* %R5_Var\r\n store i64 undef, i64* %R6_Var\r\n store float undef, float* %F1_Var\r\n store double undef, double* %D1_Var\r\n store float undef, float* %F2_Var\r\n store double undef, double* %D2_Var\r\n store float undef, float* %F3_Var\r\n store double undef, double* %D3_Var\r\n store float undef, float* %F4_Var\r\n store double undef, double* %D4_Var\r\n store float undef, float* %F5_Var\r\n store double undef, double* %D5_Var\r\n store float undef, float* %F6_Var\r\n store double undef, double* %D6_Var\r\n %ln7bu = call ccc double (double) @llvm.sin.f64( double %ln7bs ) nounwind\r\n}}}\r\n\r\nWhere the Rx/Fx/Dx registers are chosen based on what physical register those global registers map to, and whether those physical registers are caller-saved according to the C ABI.\r\n\r\nI'm certain this is unnecessary, as there is no way to know the physical register corresponding to those vars within an LLVM IR function. The calling convention used for the FFI call will preserve caller-saved registers as needed, i.e., only if such a value is needed after the call.","type_of_failure":"CompileTimePerformance","blocking":[]} -->8.10.1Kavon FarvardinKavon Farvardin