Skip to content

Minimal compilation to WASM leaves some memory function artifacts #2491

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
jzabinski-dolios opened this issue Jan 6, 2022 · 12 comments
Closed
Labels
wasm WebAssembly

Comments

@jzabinski-dolios
Copy link

jzabinski-dolios commented Jan 6, 2022

New to TinyGo here.

I need a series of math functions in WebAssembly. These functions can all be called discretely by Javascript and they are completely self-sufficient: they need no other runtime features like garbage collection, debug information, etc. (They likely will not even need an import object defined in Javascript.)

I made a short example program, findJD.go.

When I compile like this:
tinygo build -o tinygo.wasm -target=wasm -gc=none -scheduler=none -no-debug ./findJD.go

And then convert the WASM to WAT to look at the output (using another tool), I can see that the following is created (among other things):

(import "wasi_snapshot_preview1" "fd_write" (func $fimport$0 (param i32 i32 i32 i32) (result i32)))
(import "env" "runtime.alloc" (func $fimport$1 (param i32 i32 i32 i32) (result i32)))
(import "env" "main.main" (func $fimport$2 (param i32 i32)))
(memory $0 2)
(data (i32.const 65536) "panic: runtime error: index out of rangeunimplemented: reallocunimplemented: posix_memalignunimplemented: aligned_allocunimplemented: malloc_usable_size")
(data (i32.const 65688) "\a4\00\01\00\00\00\00\00")
(table $0 1 1 funcref)
(global $global$0 (mut i32) (i32.const 65536))
(export "memory" (memory $0))
(export "malloc" (func $5))
(export "free" (func $6))
(export "calloc" (func $7))
(export "realloc" (func $8))
(export "posix_memalign" (func $9))
(export "aligned_alloc" (func $10))
(export "malloc_usable_size" (func $11))
(export "_start" (func $12))
(export "findJD" (func $13))

Simply put, the only thing that I am going to actually use is:

(export "findJD" (func $13))

For reference, if I write my example program in WAT by hand, compile it and convert back to WAT, I get:

(type $i32_i32_f64_=>_f64 (func (param i32 i32 f64) (result f64)))
(export "findJD" (func $0))

I want to get as close to that simplicity as I can. How do I achieve this?

@dgryski
Copy link
Member

dgryski commented Jan 6, 2022

What version of tinygo were you running this with? The dev branch has a number of wasm fixes that might solve this.

@jzabinski-dolios
Copy link
Author

This was on v0.21.0 darwin/amd64. Let me try building dev to see how it does.

@jzabinski-dolios
Copy link
Author

After cloning the package, checking out the dev branch and following these instructions to build everything, I ran this in the original folder:

../tinygo/build/tinygo build -o tinygo.wasm -target=wasm -gc=none -scheduler=none -no-debug ./findJD.go

The result:

(import "wasi_snapshot_preview1" "fd_write" (func $fimport$0 (param i32 i32 i32 i32) (result i32)))
(import "env" "runtime.alloc" (func $fimport$1 (param i32 i32 i32 i32) (result i32)))
(import "env" "runtime.realloc" (func $fimport$2 (param i32 i32 i32 i32) (result i32)))
(import "env" "main.main" (func $fimport$3 (param i32 i32)))
(memory $0 2)
(data (i32.const 65536) "panic: runtime error: index out of rangeunimplemented: posix_memalignunimplemented: aligned_allocunimplemented: malloc_usable_size")
(data (i32.const 65668) "\90\00\01\00\00\00\00\00")
(table $0 1 1 funcref)
(global $global$0 (mut i32) (i32.const 65536))
(export "memory" (memory $0))
(export "malloc" (func $16))
(export "free" (func $17))
(export "calloc" (func $18))
(export "realloc" (func $19))
(export "posix_memalign" (func $20))
(export "aligned_alloc" (func $21))
(export "malloc_usable_size" (func $22))
(export "_start" (func $23))
(export "findJD" (func $24))

Basically the issue reproduces. (runtime.reallocwas added.)

The original compilation target's code:

package main

// FloorDiv returns the integer floor of the fractional value (x / y).
//
// It uses integer math only, so is more efficient than using floating point
// intermediate values.  This function can be used in many places where INT()
// appears in AA.  As with built in integer division, it panics with y == 0.
func FloorDiv(x, y int) (q int) {
	q = x / y
	if (x < 0) != (y < 0) && x%y != 0 {
		q--
	}
	return
}

// FloorDiv64 returns the integer floor of the fractional value (x / y).
//
// It uses integer math only, so is more efficient than using floating point
// intermediate values.  This function can be used in many places where INT()
// appears in AA.  As with built in integer division, it panics with y == 0.
func FloorDiv64(x, y int64) (q int64) {
	q = x / y
	if (x < 0) != (y < 0) && x%y != 0 {
		q--
	}
	return
}

// CalendarGregorianToJD converts a Gregorian year, month, and day of month
// to Julian day.
//
// Negative years are valid, back to JD 0.  The result is not valid for
// dates before JD 0.
//export findJD
func CalendarGregorianToJD(y, m int, d float64) float64 {
	switch m {
	case 1, 2:
		y--
		m += 12
	}
	a := FloorDiv(y, 100)
	b := 2 - a + FloorDiv(a, 4)
	// (7.1) p. 61
	return float64(FloorDiv64(36525*(int64(y+4716)), 100)) +
		float64(FloorDiv(306*(m+1), 10)+b) + d - 1524.5
}

@dgryski
Copy link
Member

dgryski commented Jan 6, 2022

Hmm. I was hoping the wasm-opt pass would handle some of that tree shaking. I'll investigate this a bit more.

@fgsch
Copy link
Contributor

fgsch commented Jan 6, 2022

Part of this is related to panic handling. You can remove those passing -panic trap to tinygo build.

@jzabinski-dolios
Copy link
Author

jzabinski-dolios commented Jan 6, 2022

That does improve things somewhat.

Ran this:
tinygo build -o tinygo.wasm -target=wasm -gc=none -scheduler=none -no-debug -panic=trap ./findJD.go

The file size went from around 1000 bytes to 555 bytes, and the quoted section becomes:

(import "env" "runtime.alloc" (func $fimport$0 (param i32 i32 i32 i32) (result i32)))
(import "env" "main.main" (func $fimport$1 (param i32 i32)))
(memory $0 1)
(table $0 1 1 funcref)
(global $global$0 (mut i32) (i32.const 65536))
(export "memory" (memory $0))
(export "malloc" (func $0))
(export "free" (func $1))
(export "calloc" (func $2))
(export "realloc" (func $3))
(export "posix_memalign" (func $4))
(export "aligned_alloc" (func $5))
(export "malloc_usable_size" (func $6))
(export "_start" (func $7))
(export "findJD" (func $8))

@aykevl
Copy link
Member

aykevl commented Jan 19, 2022

I have removed a few in #2549.

At the moment we only really support WASI or browser environments, not stand alone environments. This might be something we could add though (with limitations such as not being able to use println and having to call a runtime initializer at least once).

@jzabinski-dolios
Copy link
Author

@aykevl : could you please clarify what you mean by 'stand alone environments'?

I think that you mean that TinyGo isn't designed to support WASM files that only contain functions exported via the export keyword. Some runtime functionality will always be needed (for things like linear memory initialization).

Of course such environments need to be embedded somewhere like a browser, so to me 'stand alone environment' can't be exclusive of an embedding browser or WASI environment. Thus my question.

@ramilexe
Copy link

HI @aykevl
I am interested to build wasm for a standalone environment + some helper to pass string/bytes between wasm and js (read/write from memory). Could you give me some thought about what needs to be implemented for that?

@codefromthecrypt
Copy link
Contributor

freestanding seems to be the latest on getting this solved. #3068

@jzabinski-dolios if you agree, do you mind closing this out for that one, mainly as it is more elaborated and with a work-in-progress PR?

@jzabinski-dolios
Copy link
Author

Don't mind at all, thanks for asking

@aykevl
Copy link
Member

aykevl commented Sep 8, 2022

I think this issue is slightly different. I agree the memory function artifacts are a problem. I have a fix for it here: #3142. It reduces binary size significantly for small binaries that don't allocate (and use -scheduler=none).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
wasm WebAssembly
Projects
None yet
Development

No branches or pull requests

7 participants