Skip to content

Commit 9f275d9

Browse files
committed
FIXED: Reduce constants to NF before specialisation [#3129](#3129)
1 parent 0b6aa0d commit 9f275d9

File tree

4 files changed

+48
-5
lines changed

4 files changed

+48
-5
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
FIXED: Reduce constants to NF before specialization [#3129](https://github.com/clash-lang/clash-compiler/issues/3129)

clash-lib/src/Clash/Normalize/Transformations/Specialize.hs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Copyright : (C) 2012-2016, University of Twente,
33
2016-2017, Myrtle Software Ltd,
44
2017-2018, Google Inc.,
5-
2021-2024, QBayLogic B.V.
5+
2021-2026, QBayLogic B.V.
66
License : BSD2 (see the file LICENSE)
77
Maintainer : QBayLogic B.V. <devops@qbaylogic.com>
88
@@ -87,7 +87,7 @@ import Clash.Rewrite.Types
8787
, typeTranslator, workFreeBinders, debugOpts, topEntities, specializationLimit)
8888
import Clash.Rewrite.Util
8989
( mkBinderFor, mkDerivedName, mkFunction, mkTmBinderFor, setChanged, changed
90-
, normalizeTermTypes, normalizeId)
90+
, normalizeTermTypes, normalizeId, whnfRW)
9191
import Clash.Rewrite.WorkFree (isWorkFree)
9292
import Clash.Normalize.Types
9393
( NormRewrite, NormalizeSession, specialisationCache, specialisationHistory)
@@ -290,9 +290,17 @@ constantSpec ctx@(TransformContext is0 tfCtx) e@(App e1 e2)
290290
= do specInfo<- constantSpecInfo ctx e2
291291
if csrFoundConstant specInfo then
292292
let newBindings = csrNewBindings specInfo in
293-
if null newBindings then
294-
-- Whole of e2 is constant
295-
specialize ctx (App e1 e2)
293+
if null newBindings then do
294+
-- Whole of e2 is constant, we reduce it here eagerly before specialization
295+
-- because we have observed long running compile times when having
296+
-- recursively defined constants, see:
297+
-- https://github.com/clash-lang/clash-compiler/issues/3129
298+
e2Red <- case collectArgs e2 of
299+
(Prim p0, _) -> whnfRW False ctx e2 $ \_ctx1 e2Red -> case e2Red of
300+
(collectArgs -> (Prim p1, _)) | primName p0 == primName p1 -> return e2
301+
_ -> changed e2Red
302+
_ -> return e2
303+
specialize ctx (App e1 e2Red)
296304
else do
297305
-- Parts of e2 are constant
298306
let is1 = extendInScopeSetList is0 (fst <$> csrNewBindings specInfo)

tests/Main.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,7 @@ runClashTest = defaultMain
962962
, outputTest "PortNamesWithRTree" def{hdlTargets=[Verilog]}
963963
, clashLibTest "T1182A" def
964964
, clashLibTest "T1182B" def
965+
, runTest "T3129" def{hdlSim=[], clashFlags=["-fclash-spec-limit=400"]}
965966
]
966967
, clashTestGroup "Unit"
967968
[ runTest "Imap" def
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
module T3129 where
2+
3+
import Clash.Prelude
4+
5+
import GHC.Base (isTrue#)
6+
import GHC.Num.Natural (Natural (NS), naturalLogBase#)
7+
import GHC.Prim (eqWord#, plusWord#)
8+
9+
naturalCLogBase :: Natural -> Natural -> Natural
10+
naturalCLogBase x y =
11+
let z1 = naturalLogBase# x y
12+
z2 = naturalLogBase# x (y-1)
13+
in case y of
14+
1 -> 0
15+
_ | isTrue# (z1 `eqWord#` z2) -> NS (z1 `plusWord#` 1##)
16+
| otherwise -> NS z1
17+
18+
f :: Natural -> Natural
19+
f m = 2 * (naturalCLogBase 2 m)
20+
21+
g :: Natural -> Natural
22+
g m =
23+
let i = f m
24+
calc 0 _ _ = 1
25+
calc 1 val tmp = (val * tmp) `mod` m
26+
calc n val tmp = calc
27+
(if n `mod` 2 == 0 then n `div` 2 else n - 1 )
28+
(if n `mod` 2 == 0 then val * val `mod` m else val )
29+
(if n `mod` 2 == 0 then tmp `mod` m else (tmp * val) `mod` m)
30+
in calc (m - i - 1) 2 1
31+
32+
topEntity :: Natural
33+
topEntity = g 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff

0 commit comments

Comments
 (0)