calyxir / calyx

Intermediate Language (IL) for Hardware Accelerator Generators
https://calyxir.org
MIT License
492 stars 50 forks source link

Running `-x inline:always` generates internal compiler error #1283

Closed calebmkim closed 1 year ago

calebmkim commented 1 year ago

Always inlining gives an error in the following Calyx design (this is coming up in the neural network designs, but this is just the minimal case of it):

l.futil.txt

The error is:

thread 'main' panicked at 'Weak reference points to nothing', calyx/src/ir/common.rs:23:14
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Basically, there is a component, fp_pow, which itself contains an invoke of a register. That component is read from in the main, which is giving an error when I try to fully inline. I've been trying to figure out what's wrong w/ it but if anyone else that understands the component-inliner pass better could give their input that would help.

rachitnigam commented 1 year ago

What’s the error message?

rachitnigam commented 1 year ago

The invoke to the register must be compiler into a group since primitive invoked cannot be compiled. I’d recommend disabling group2invoke

calebmkim commented 1 year ago

sounds good that's what I've been doing. (Also, I've updated the original issue comment to include the error message)

rachitnigam commented 1 year ago

Run with backtrace to see the stack trace

calebmkim commented 1 year ago

The error is happening in the rewrite_invoke() function. Specifically, it occurs when we are going through the inputs of the invoke and we are trying to get the parent name of one of the input ports, that's when it looks like the error is happening.

rachitnigam commented 1 year ago

Can you try looking to which port is the problem? One option is to use VS Code's debugger to step through the program. You can use the "Debug Compiler" option to provide the arguments and the file and step through the program to see where the bug is coming from

rachitnigam commented 1 year ago

The invoke to the register must be compiler into a group since primitive invoked cannot be compiled. I’d recommend disabling group2invoke

Also, to be clear, this is problem with how -x inline:always works and interacts with group2invoke. We should expose some option like -x group2invoke:skip-primitives to ensure that it doesn't turn primitive groups to invokes so that always inlining works correctly

sampsyo commented 1 year ago

Just tacking on an additional probably-wrong thought, just in case: instead of telling group2invoke not to deal with primitives, would an alternative be to tell the inline:always mode to not actually attempt to inline primitives?

calebmkim commented 1 year ago

Ok, I think I may have a guess for what's going on?

component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) {
  cells {
    c = some_user_defined_comp();
    r = std_reg(32);
  }
  wires {
  }
  control {
    seq {
      invoke r(in = c.out)();
    }
  }
}

The error is happening when we are at the invoke r(in = c.out)(). Specifically, when re come across in = c.out, we try to get_parent_name() on the port c.out. But (I think) since we've already inlined the component c, the parent cell for the port c.out is pointing to nothing? However, if we replace r to be a user defined component (not a primitive) the program compiles fine; I think because when we actually inline this user defined component, the pass works fine. It just behaves weirdly when we can't inline it (which the register case falls under).

rachitnigam commented 1 year ago

The version of the program with only components doesn't error out:

import "primitives/core.futil";

component foo() ->  (out: 32) {
  cells {}
  wires {}
  control {}
}
component bar(in: 32) ->  (out: 32) {
  cells {}
  wires {}
  control {}
}
component main(@go go: 1, @clk clk: 1, @reset reset: 1) -> (@done done: 1) {
  cells {
    c = foo();
    b = bar();
  }
  wires {
  }
  control {
    seq {
      invoke b(in = c.out)();
    }
  }
}
rachitnigam commented 1 year ago

I looked at the error and it seems to occur during the final program printing and not the pass itself. This means that the likely problem is that the inlining pass is not rewriting the assignment at all and when the pretty printer attempt to print c.out, it realizes that c is no longer defined