Skip to content

bugc: Block.predecessors includes call edges only after inlining #394

Open

Description

@gnidan

Block.predecessors means two different things depending on whether inlining touched a function.

  • irgen leaves a call's edge to its continuation out on purpose (packages/bugc/src/irgen/generate/function.ts:138-143: block merging, jump optimization, return merging and tail calls read predecessors without call edges).
  • InliningStep recomputes the caller's predecessors with call edges (recomputePredecessors in packages/bugc/src/optimizer/steps/inlining.ts:714-719, via successors, which includes call → continuation). irgen/generate/process.ts:82 also adds them, before function.ts recomputes.

Example at -O2 (f is recursive, so not inlined; g is inlined):

name Preds;
define {
  function g(n: uint256) -> uint256 { return n + 1; };
  function f(n: uint256) -> uint256 {
    if (n == 0) { return 0; }
    r = r + 1;
    return f(n - 1);
  };
}
storage { [0] r: uint256; }
create {}
code {
  r = g(msg.value);   // remove this line for the other case
  f(msg.data.length);
  r = r + 5;
}

With the g line, main's call_cont_2 has predecessors ["call_cont_1"] (the calling block). Without it, call_cont_1 has []. The passes that count predecessors (block merging's size === 1 checks, tail calls' "other predecessors" of a continuation) can therefore decide differently for the same shape of CFG. No miscompile found yet; this is about picking one meaning and making both producers agree.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions