Skip to content

Commit

Permalink
wasm: replace unused functions by stub (#3206)
Browse files Browse the repository at this point in the history
We're in this situation: performing dead code analysis on wasm isn't too
hard, but it requires a representation of all wasm instructions: we'd need
to be able to parse the "runtime" wasm bits, i.e., what's built using llvm
from C code. When building upon that wasm module, we process the function
bodies uninterpreted -- they are all just `[]byte` to us.

This restriction lets us get by without implementing all the wasm
instructions -- we only write what we use, and read a bare minimum to work
as outlined above.

To still be able to remove dead code, this change employs a trick: at build
time, when the aforementioned runtime wasm module is compiled, we're calling
wasm-opt on it to extract its call graph. We'll use that, together with the
functions actually planned in our wasm compiler (using the subset of
instructions that we understand), to remove all unused functions from the
name section, and replace their function bodies with `unreachable`.

We cannot really remove them, since that would require reindexing all
functions; and we cannot do that without replacing the function indices at
their call sites in the "runtime" wasm module.

Another restriction to the impact of this approach is call_indirect: We
need to keep every function that's referenced in the table -- we don't know
which function might be calling them indirectly. In a follow-up, we could
record that information and use it to further reduce the code size: we know
that if none of the regex-related builtins are used, we could also stub out
the re2-related functions.

Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
  • Loading branch information
srenatus committed Mar 11, 2021
1 parent 79a5a55 commit 5a1ed9c
Show file tree
Hide file tree
Showing 9 changed files with 2,526 additions and 14 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ wasm-lib-build:
ifeq ($(DOCKER_RUNNING), 1)
@$(MAKE) -C wasm ensure-builder build
cp wasm/_obj/opa.wasm internal/compiler/wasm/opa/opa.wasm
cp wasm/_obj/callgraph.csv internal/compiler/wasm/opa/callgraph.csv
else
@echo "Docker not installed or not running. Skipping OPA-WASM library build."
endif
Expand Down
1 change: 1 addition & 0 deletions build/check-working-copy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
EXCEPTIONS=(
"internal/compiler/wasm/opa/opa.go"
"internal/compiler/wasm/opa/opa.wasm"
"internal/compiler/wasm/opa/callgraph.csv"
)

STATUS=$(git status --porcelain)
Expand Down
52 changes: 41 additions & 11 deletions internal/cmd/genopawasm/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ func main() {
Use: executable,
Short: executable + " <opa.wasm path>",
RunE: func(_ *cobra.Command, args []string) error {
if len(args) != 1 {
return fmt.Errorf("provide path of opa.wasm file")
if len(args) != 2 {
return fmt.Errorf("provide path of opa.wasm and callgraph.csv files")
}
return run(params, args)
},
Expand Down Expand Up @@ -81,6 +81,16 @@ func Bytes() ([]byte, error) {
return ioutil.ReadAll(gr)
}
// CallGraphCSV returns a CSV representation of the
// OPA-WASM bytecode's call graph: 'caller,callee'
func CallGraphCSV() ([]byte, error) {
cg, err := gzip.NewReader(bytes.NewBuffer(gzippedCallGraphCSV))
if err != nil {
return nil, err
}
return ioutil.ReadAll(cg)
}
`))

if err != nil {
Expand All @@ -92,7 +102,34 @@ func Bytes() ([]byte, error) {
return err
}

in, err := os.Open(args[0])
if err := output(out, args[0]); err != nil {
return err
}

if _, err := out.Write([]byte(`")
`)); err != nil {
return err
}

_, err = out.Write([]byte(`var gzippedCallGraphCSV = []byte("`))
if err != nil {
return err
}

if err := output(out, args[1]); err != nil {
return err
}

if _, err := out.Write([]byte(`")
`)); err != nil {
return err
}

return nil
}

func output(out io.Writer, filename string) error {
in, err := os.Open(filename)
if err != nil {
return err
}
Expand All @@ -116,14 +153,7 @@ func Bytes() ([]byte, error) {
return err
}
}

_, err = out.Write([]byte(`")`))
if err != nil {
return err
}

_, err = out.Write([]byte("\n"))
return err
return nil
}

var digits = "0123456789ABCDEF"
Expand Down

0 comments on commit 5a1ed9c

Please sign in to comment.