Andreas Klebinger pushed to branch wip/andreask/9.10.4-batch1 at Glasgow Haskell Compiler / GHC
Commits:
-
91389fdf
by Ben Gamari at 2026-10-30T11:35:27+01:00
-
8981fe7a
by sheaf at 2026-10-30T11:35:27+01:00
-
ed19e629
by mangoiv at 2026-10-30T11:35:27+01:00
-
b20f08ad
by Zubin Duggal at 2026-10-30T11:35:27+01:00
9 changed files:
- + changelog.d/T27046
- compiler/GHC/Cmm/Parser.y
- compiler/GHC/CmmToAsm/AArch64/CodeGen.hs
- libraries/process
- rts/sm/Evac.h
- testsuite/driver/testlib.py
- + testsuite/tests/codeGen/should_run/T27046.hs
- + testsuite/tests/codeGen/should_run/T27046_cmm.cmm
- testsuite/tests/codeGen/should_run/all.T
Changes:
| 1 | +section: compiler
|
|
| 2 | +issues: #27046
|
|
| 3 | +mrs: !16031
|
|
| 4 | +synopsis:
|
|
| 5 | + Avoid AArch64 register clobbering bug in MUL2
|
|
| 6 | +description:
|
|
| 7 | + Fixes an issue in which, on AArch64, code generation for the MUL2 operation
|
|
| 8 | + could clobber one of the input operands when computing the lower bits, which
|
|
| 9 | + rendered invalid the subsequent computation of the high bits. |
| ... | ... | @@ -1164,6 +1164,8 @@ callishMachOps platform = listToUFM $ |
| 1164 | 1164 | , allWidths "fetch_nand" (\w -> MO_AtomicRMW w AMO_Nand)
|
| 1165 | 1165 | , allWidths "fetch_or" (\w -> MO_AtomicRMW w AMO_Or)
|
| 1166 | 1166 | , allWidths "fetch_xor" (\w -> MO_AtomicRMW w AMO_Xor)
|
| 1167 | + , allWidths "mul2_" (\w -> MO_S_Mul2 w)
|
|
| 1168 | + , allWidths "mul2u_" (\w -> MO_U_Mul2 w)
|
|
| 1167 | 1169 | ]
|
| 1168 | 1170 | where
|
| 1169 | 1171 | allWidths
|
| ... | ... | @@ -1694,11 +1694,17 @@ genCCall target dest_regs arg_regs bid = do |
| 1694 | 1694 | let lo = getRegisterReg platform (CmmLocal dst_lo)
|
| 1695 | 1695 | hi = getRegisterReg platform (CmmLocal dst_hi)
|
| 1696 | 1696 | nd = getRegisterReg platform (CmmLocal dst_needed)
|
| 1697 | + -- Generate a fresh virtual register for the low word computation.
|
|
| 1698 | + -- This avoids clobbering reg_a or reg_b in the first MUL instruction,
|
|
| 1699 | + -- which could for example happen if 'lo' and 'reg_a' are the same
|
|
| 1700 | + -- virtual register.
|
|
| 1701 | + tmp_lo <- getNewRegNat II64
|
|
| 1697 | 1702 | return (
|
| 1698 | 1703 | code_x `appOL`
|
| 1699 | 1704 | code_y `snocOL`
|
| 1700 | - MUL (OpReg W64 lo) (OpReg W64 reg_a) (OpReg W64 reg_b) `snocOL`
|
|
| 1705 | + MUL (OpReg W64 tmp_lo) (OpReg W64 reg_a) (OpReg W64 reg_b) `snocOL`
|
|
| 1701 | 1706 | SMULH (OpReg W64 hi) (OpReg W64 reg_a) (OpReg W64 reg_b) `snocOL`
|
| 1707 | + MOV (OpReg W64 lo) (OpReg W64 tmp_lo) `snocOL`
|
|
| 1702 | 1708 | -- Are all high bits equal to the sign bit of the low word?
|
| 1703 | 1709 | -- nd = (hi == ASR(lo,width-1)) ? 1 : 0
|
| 1704 | 1710 | CMP (OpReg W64 hi) (OpRegShift W64 lo SASR (widthInBits w - 1)) `snocOL`
|
| 1 | -Subproject commit f7d51387ba7f7f6079f3a9d5ce011ad9359b7dbb |
|
| 1 | +Subproject commit 11fd247ad33208da7a914acf15d4a09d64a6a4c4 |
| ... | ... | @@ -6,7 +6,7 @@ |
| 6 | 6 | *
|
| 7 | 7 | * Documentation on the architecture of the Garbage Collector can be
|
| 8 | 8 | * found in the online commentary:
|
| 9 | - *
|
|
| 9 | + *
|
|
| 10 | 10 | * https://gitlab.haskell.org/ghc/ghc/wikis/commentary/rts/storage/gc
|
| 11 | 11 | *
|
| 12 | 12 | * ---------------------------------------------------------------------------*/
|
| ... | ... | @@ -25,7 +25,9 @@ |
| 25 | 25 | // registers EAX, EDX, and ECX instead of on the stack. Functions that
|
| 26 | 26 | // take a variable number of arguments will continue to be passed all of
|
| 27 | 27 | // their arguments on the stack.
|
| 28 | -#if __GNUC__ >= 2 && (defined(x86_64_HOST_ARCH) || defined(i386_HOST_ARCH))
|
|
| 28 | +// On x86-64 the attribute has no effect (the first argument is already
|
|
| 29 | +// passed in a register) and GCC 16 warns that it is ignored.
|
|
| 30 | +#if defined(i386_HOST_ARCH)
|
|
| 29 | 31 | #define REGPARM1 __attribute__((regparm(1)))
|
| 30 | 32 | #else
|
| 31 | 33 | #define REGPARM1
|
| ... | ... | @@ -2723,7 +2723,19 @@ def normalise_errmsg(s: str) -> str: |
| 2723 | 2723 | # clang may warn about unused argument when used as assembler
|
| 2724 | 2724 | s = re.sub('.*warning: argument unused during compilation:.*\n', '', s)
|
| 2725 | 2725 | # Emscripten displays cache info and old emcc doesn't support EMCC_LOGGING=0
|
| 2726 | - s = re.sub('cache:INFO: .*\n', '', s)
|
|
| 2726 | + s = drop_lines_containing(s, 'cache:INFO:')
|
|
| 2727 | + # Old emcc warns when we export HEAP8 but new one requires it (see #26290)
|
|
| 2728 | + s = s.replace('warning: invalid item in EXPORTED_RUNTIME_METHODS: HEAP8\nwarning: invalid item in EXPORTED_RUNTIME_METHODS: HEAPU8\nemcc: warning: warnings in JS library compilation [-Wjs-compiler]\n','')
|
|
| 2729 | + |
|
| 2730 | + if opsys('darwin'):
|
|
| 2731 | + # on newer versions of MacOS X, the shipped ranlib warns about object files with no symbols,
|
|
| 2732 | + # however, these are completely benign stubs.
|
|
| 2733 | + # See https://gitlab.haskell.org/ghc/ghc/-/issues/27116
|
|
| 2734 | + s = modify_lines(s, lambda l: re.sub(r'.*ranlib:.*has no symbols', '', l))
|
|
| 2735 | + # we also want to remove linker warnings having to do with undefined dynamic_lookup in combination with
|
|
| 2736 | + # making a single weak symbol undefined as this is dependent on other linker flags
|
|
| 2737 | + # See also https://github.com/haskell/process/pull/377
|
|
| 2738 | + s = drop_lines_containing(s, 'ld: warning: -U option is redundant when using -undefined dynamic_lookup')
|
|
| 2727 | 2739 | |
| 2728 | 2740 | return s
|
| 2729 | 2741 |
| 1 | +{-# LANGUAGE MagicHash #-}
|
|
| 2 | +{-# LANGUAGE ForeignFunctionInterface, GHCForeignImportPrim, UnliftedFFITypes #-}
|
|
| 3 | + |
|
| 4 | +module Main where
|
|
| 5 | + |
|
| 6 | +import Control.Monad
|
|
| 7 | + ( unless )
|
|
| 8 | +import Data.Bits
|
|
| 9 | + ( shiftL )
|
|
| 10 | +import GHC.Exts
|
|
| 11 | + ( Int64# )
|
|
| 12 | +import GHC.Int
|
|
| 13 | + ( Int64(..) )
|
|
| 14 | + |
|
| 15 | +foreign import prim "test_mul2_clobber"
|
|
| 16 | + test_mul2_clobber :: Int64# -> Int64# -> Int64#
|
|
| 17 | + |
|
| 18 | +main :: IO ()
|
|
| 19 | +main = do
|
|
| 20 | + let
|
|
| 21 | + I64# x = 1 `shiftL` 32
|
|
| 22 | + hi = I64# $ test_mul2_clobber x x
|
|
| 23 | + |
|
| 24 | + unless ( hi == 1 ) $
|
|
| 25 | + error $ unlines
|
|
| 26 | + [ "Incorrect result for Mul2 operation."
|
|
| 27 | + , "Expected high word: 1"
|
|
| 28 | + , " Actual high word: " ++ show hi
|
|
| 29 | + ] |
| 1 | +#include "Cmm.h"
|
|
| 2 | + |
|
| 3 | +// Test for #27046
|
|
| 4 | +test_mul2_clobber (bits64 x, bits64 y)
|
|
| 5 | +{
|
|
| 6 | + bits64 hi, nd;
|
|
| 7 | + |
|
| 8 | + // Deliberately alias the destination 'lo' with the source 'x'
|
|
| 9 | + // This forces the NCG to use the same virtual register for both.
|
|
| 10 | + (nd, hi, x) = prim %mul2_64(x, y);
|
|
| 11 | + |
|
| 12 | + return (hi);
|
|
| 13 | +} |
| ... | ... | @@ -253,6 +253,13 @@ test('T24893', normal, compile_and_run, ['-O']) |
| 253 | 253 | test('T26061', normal, compile_and_run, [''])
|
| 254 | 254 | test('T26537', normal, compile_and_run, ['-O2 -fregs-graph'])
|
| 255 | 255 | test('T24016', normal, compile_and_run, ['-O1 -fPIC'])
|
| 256 | +test('T27046',
|
|
| 257 | + [ req_cmm
|
|
| 258 | + , when(arch('i386'), skip) # i386 does not support MO_S_Mul2 W64
|
|
| 259 | + , when(arch('wasm32'), skip)
|
|
| 260 | + , js_skip
|
|
| 261 | + , when(unregisterised(), skip) # pprCallishMachOp_for_C: MO_S_Mul2 W64 not supported
|
|
| 262 | + ], compile_and_run, ['T27046_cmm.cmm'])
|
|
| 256 | 263 | |
| 257 | 264 | # Check that GHC-generated finalizers run on Darwin. The Apple linker doesn't
|
| 258 | 265 | # support --wrap, so we can't intercept hs_spt_remove directly. Instead we
|