diff --git a/compiler/GHC/CmmToC.hs b/compiler/GHC/CmmToC.hs index 972095267b90a5d307d09812b7ffe241420fff03..8b1306f1f2d9d189e7d64fd692ab603adcc9b68d 100644 --- a/compiler/GHC/CmmToC.hs +++ b/compiler/GHC/CmmToC.hs @@ -464,9 +464,29 @@ However, this would be wrong; by widening `b` directly from `StgInt8` to `StgWord` we will get sign-extension semantics: rather than 0xf6 we will get 0xfffffffffffffff6. To avoid this we must first cast `b` back to `StgWord8`, ensuring that we get zero-extension semantics when we widen up to `StgWord`. + +Note [When in doubt, cast arguments as unsigned] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +In general C's signed-ness behavior can lead to surprising results and +consequently we are very explicit about ensuring that arguments have the +correct signedness. For instance, consider a program like + + test() { + bits64 ret, a, b; + a = %neg(43 :: bits64); + b = %neg(0x443c70fa3e465120 :: bits64); + ret = %modu(a, b); + return (ret); + } + +In this case both `a` and `b` will be StgInts in the generated C (since +`MO_Neg` is a signed operation). However, we want to ensure that we perform an +*unsigned* modulus operation, therefore we must be careful to cast both arguments +to StgWord. -} --- | The type of most operations is determined by the operands. However, there are a few exceptions. For these we explicitly cast the result. +-- | The result type of most operations is determined by the operands. However, +-- there are a few exceptions. For these we explicitly cast the result. machOpNeedsCast :: Platform -> MachOp -> [CmmType] -> Maybe SDoc machOpNeedsCast platform mop args -- Comparisons in C have type 'int', but we want type W_ (this is what @@ -500,9 +520,13 @@ pprMachOpApp' platform mop args where -- Cast needed for signed integer ops - pprArg e | signedOp mop = cCast platform (machRep_S_CType platform (typeWidth (cmmExprType platform e))) e - | needsFCasts mop = cCast platform (machRep_F_CType (typeWidth (cmmExprType platform e))) e - | otherwise = pprExpr1 platform e + pprArg e + | signedOp mop = cCast platform (machRep_S_CType platform width) e + | needsFCasts mop = cCast platform (machRep_F_CType width) e + -- See Note [When in doubt, cast arguments as unsigned] + | otherwise = cCast platform (machRep_U_CType platform width) e + where + width = typeWidth (cmmExprType platform e) needsFCasts (MO_F_Eq _) = False needsFCasts (MO_F_Ne _) = False needsFCasts (MO_F_Neg _) = True diff --git a/testsuite/tests/cmm/should_run/machops/MachOps1.cmm b/testsuite/tests/cmm/should_run/machops/MachOps1.cmm new file mode 100644 index 0000000000000000000000000000000000000000..e5d33ef285687f1ecbc04f27830fd259d689ace4 --- /dev/null +++ b/testsuite/tests/cmm/should_run/machops/MachOps1.cmm @@ -0,0 +1,3 @@ +test(bits64 buffer) { + return (%modu(%sx64((72 :: bits32)), %modu((-43 :: bits64), (-0x443c70fa3e465120 :: bits64)))); +} diff --git a/testsuite/tests/cmm/should_run/machops/MachOps1.stdout b/testsuite/tests/cmm/should_run/machops/MachOps1.stdout new file mode 100644 index 0000000000000000000000000000000000000000..ea70ce013438b2e42d5297cf39110708d9cfe033 --- /dev/null +++ b/testsuite/tests/cmm/should_run/machops/MachOps1.stdout @@ -0,0 +1 @@ +72 diff --git a/testsuite/tests/cmm/should_run/machops/all.T b/testsuite/tests/cmm/should_run/machops/all.T index faad54a2ce3f6e57b1d4ecb7956c1b70bd3d2a67..d705f331dd8095883f4dbdf014fee699d119e4ed 100644 --- a/testsuite/tests/cmm/should_run/machops/all.T +++ b/testsuite/tests/cmm/should_run/machops/all.T @@ -8,3 +8,4 @@ cmm_test('T20626a') cmm_test('T20626b') cmm_test('T20638') cmm_test('T20634') +cmm_test('MachOps1')