Semantics of Cmm `switch` statement?

For testing purposes, I created the following Cmm program:
myswitch (bits32 n) {
switch [0 .. 4] n {
case 0, 1: { foreign "C" A(); }
case 2: { foreign "C" B(); }
case 4: { foreign "C" C(); }
default: { foreign "C" D(); }
}
return (666);
}
In the original C-- specification, it's pretty clear that when, say,
the call to foreign function `A` terminates, the switch statement is
supposed to finish and function `myswitch` is supposed to return 666.
What actually happens in GHC is that this source code is parsed into a
control-flow graph in which execution loops forever, repeating the
call. The relevant fragment of the prettyprinted CFG looks like this:
{offset
ca: // global
_c1::I32 = %MO_XX_Conv_W64_W32(R1);
//tick src

Hi Norman, I vaguely remember that we "finish" such unterminated code blocks by jumping to the block again. That is for code like this: myswitch2 (bits32 n) { foreign "C" D(); } We produce code like this: { cg: call "ccall" arg hints: [] result hints: [] D(); goto cg; } Instead of blowing up the compiler at compile time or the program at runtime. For switch statements I think blocks are just syntactic sugar. E.g. if you write case n: { <code> } it's treated as if you wrote case n: jmp codeBlock; ... codeBlock: <code> And since your blocks don't terminate we get the behaviour you are seeing. But I haven't looked at any of the code related to this so it's possible I got it wrong. Cheers Andreas Am 12/01/2022 um 01:02 schrieb Norman Ramsey:
For testing purposes, I created the following Cmm program:
myswitch (bits32 n) { switch [0 .. 4] n { case 0, 1: { foreign "C" A(); } case 2: { foreign "C" B(); } case 4: { foreign "C" C(); } default: { foreign "C" D(); } } return (666); }
In the original C-- specification, it's pretty clear that when, say, the call to foreign function `A` terminates, the switch statement is supposed to finish and function `myswitch` is supposed to return 666. What actually happens in GHC is that this source code is parsed into a control-flow graph in which execution loops forever, repeating the call. The relevant fragment of the prettyprinted CFG looks like this:
{offset ca: // global _c1::I32 = %MO_XX_Conv_W64_W32(R1); //tick src
switch [0 .. 4] _c1::I32 { case 0, 1 : goto c5; case 2 : goto c7; case 4 : goto c9; default: {goto c3;} } ... c5: // global //tick src _c4::I64 = A; call "ccall" arg hints: [] result hints: [] (_c4::I64)(); goto c5; ... } Surprising, at least to me.
Is this behavior a bug or a feature? And if it is a feature, can anyone explain it to me?
Norman _______________________________________________ ghc-devs mailing list ghc-devs@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs
participants (2)
-
Andreas Klebinger
-
Norman Ramsey