import Digraph ( SCC(..), stronglyConnComp )
import ListSetOps ( assocDefault )
import Util ( filterOut, sortLe )
-import CmdLineOpts ( DynFlags )
+import CmdLineOpts ( DynFlags(..), HscTarget(..) )
import FastString ( LitString, FastString, unpackFS )
import Outputable
Nothing -> return Nothing
Just stmts -> do id <- forkCgStmts stmts; return (Just id)
+ ; dflags <- getDynFlags
+ ; let via_C | HscC <- hscTarget dflags = True
+ | otherwise = False
+
; stmts <- mk_switch tag_expr (sortLe le branches)
- mb_deflt_id lo_tag hi_tag
+ mb_deflt_id lo_tag hi_tag via_C
; emitCgStmts stmts
}
where
mk_switch :: CmmExpr -> [(ConTagZ, CgStmts)]
- -> Maybe BlockId -> ConTagZ -> ConTagZ
+ -> Maybe BlockId -> ConTagZ -> ConTagZ -> Bool
-> FCode CgStmts
-- SINGLETON TAG RANGE: no case analysis to do
-mk_switch tag_expr [(tag,stmts)] _ lo_tag hi_tag
+mk_switch tag_expr [(tag,stmts)] _ lo_tag hi_tag via_C
| lo_tag == hi_tag
= ASSERT( tag == lo_tag )
return stmts
-- SINGLETON BRANCH, NO DEFUALT: no case analysis to do
-mk_switch tag_expr [(tag,stmts)] Nothing lo_tag hi_tag
+mk_switch tag_expr [(tag,stmts)] Nothing lo_tag hi_tag via_C
= return stmts
-- The simplifier might have eliminated a case
-- so we may have e.g. case xs of
-- can't happen, so no need to test
-- SINGLETON BRANCH: one equality check to do
-mk_switch tag_expr [(tag,stmts)] (Just deflt) lo_tag hi_tag
+mk_switch tag_expr [(tag,stmts)] (Just deflt) lo_tag hi_tag via_C
= return (CmmCondBranch cond deflt `consCgStmt` stmts)
where
cond = cmmNeWord tag_expr (CmmLit (mkIntCLit tag))
-- the branches is the tag 0, because comparing '== 0' is likely to be
-- more efficient than other kinds of comparison.
--- DENSE TAG RANGE: use a switch statment
-mk_switch tag_expr branches mb_deflt lo_tag hi_tag
- | use_switch -- Use a switch
+-- DENSE TAG RANGE: use a switch statment.
+--
+-- We also use a switch uncoditionally when compiling via C, because
+-- this will get emitted as a C switch statement and the C compiler
+-- should do a good job of optimising it. Also, older GCC versions
+-- (2.95 in particular) have problems compiling the complicated
+-- if-trees generated by this code, so compiling to a switch every
+-- time works around that problem.
+--
+mk_switch tag_expr branches mb_deflt lo_tag hi_tag via_C
+ | use_switch || via_C -- Use a switch
= do { branch_ids <- mapM forkCgStmts (map snd branches)
; let
tagged_blk_ids = zip (map fst branches) (map Just branch_ids)
= do { (assign_tag, tag_expr') <- assignTemp' tag_expr
; let cond = cmmULtWord tag_expr' (CmmLit (mkIntCLit lowest_branch))
branch = CmmCondBranch cond deflt
- ; stmts <- mk_switch tag_expr' branches mb_deflt lowest_branch hi_tag
+ ; stmts <- mk_switch tag_expr' branches mb_deflt
+ lowest_branch hi_tag via_C
; return (assign_tag `consCgStmt` (branch `consCgStmt` stmts))
}
= do { (assign_tag, tag_expr') <- assignTemp' tag_expr
; let cond = cmmUGtWord tag_expr' (CmmLit (mkIntCLit highest_branch))
branch = CmmCondBranch cond deflt
- ; stmts <- mk_switch tag_expr' branches mb_deflt lo_tag highest_branch
+ ; stmts <- mk_switch tag_expr' branches mb_deflt
+ lo_tag highest_branch via_C
; return (assign_tag `consCgStmt` (branch `consCgStmt` stmts))
}
| otherwise -- Use an if-tree
= do { (assign_tag, tag_expr') <- assignTemp' tag_expr
-- To avoid duplication
- ; lo_stmts <- mk_switch tag_expr' lo_branches mb_deflt lo_tag (mid_tag-1)
- ; hi_stmts <- mk_switch tag_expr' hi_branches mb_deflt mid_tag hi_tag
+ ; lo_stmts <- mk_switch tag_expr' lo_branches mb_deflt
+ lo_tag (mid_tag-1) via_C
+ ; hi_stmts <- mk_switch tag_expr' hi_branches mb_deflt
+ mid_tag hi_tag via_C
; lo_id <- forkCgStmts lo_stmts
; let cond = cmmULtWord tag_expr' (CmmLit (mkIntCLit mid_tag))
branch_stmt = CmmCondBranch cond lo_id