Skip to content
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

sync: add OnceFunc(), OnceValue(), and OnceValues() functions as needed for Windows using Go 1.21 #3837

Closed
wants to merge 9 commits into from
Closed
4 changes: 2 additions & 2 deletions .github/workflows/build-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
go-version: '1.21.0-rc.2'
cache: true
- name: Restore LLVM source cache
uses: actions/cache/restore@v3
Expand Down Expand Up @@ -126,7 +126,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
go-version: '1.21.0-rc.2'
cache: true
- name: Build TinyGo
run: go install
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
# statically linked binary.
runs-on: ubuntu-latest
container:
image: golang:1.20-alpine
image: golang:1.21rc2-alpine
steps:
- name: Install apk dependencies
# tar: needed for actions/cache@v3
Expand Down Expand Up @@ -135,7 +135,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
go-version: '1.21.0-rc.2'
cache: true
- name: Install wasmtime
run: |
Expand Down Expand Up @@ -177,7 +177,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
go-version: '1.21.0-rc.2'
cache: true
- name: Install Node.js
uses: actions/setup-node@v3
Expand Down Expand Up @@ -290,7 +290,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
go-version: '1.21.0-rc.2'
cache: true
- name: Restore LLVM source cache
uses: actions/cache/restore@v3
Expand Down Expand Up @@ -407,7 +407,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
go-version: '1.21.0-rc.2'
cache: true
- name: Restore LLVM source cache
uses: actions/cache/restore@v3
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
go-version: '1.21.0-rc.2'
cache: true
- name: Restore cached LLVM source
uses: actions/cache/restore@v3
Expand Down Expand Up @@ -143,7 +143,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
go-version: '1.21.0-rc.2'
cache: true
- name: Download TinyGo build
uses: actions/download-artifact@v2
Expand Down Expand Up @@ -173,7 +173,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
go-version: '1.21.0-rc.2'
cache: true
- name: Download TinyGo build
uses: actions/download-artifact@v2
Expand Down Expand Up @@ -209,7 +209,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
go-version: '1.21.0-rc.2'
cache: true
- name: Download TinyGo build
uses: actions/download-artifact@v2
Expand Down
2 changes: 1 addition & 1 deletion builder/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
if err != nil {
return nil, err
}
if major != 1 || minor < 18 || minor > 20 {
if major != 1 || minor < 18 || minor > 21 {
// Note: when this gets updated, also update the Go compatibility matrix:
// https://github.com/tinygo-org/tinygo-site/blob/dev/content/docs/reference/go-compat-matrix.md
return nil, fmt.Errorf("requires go version 1.18 through 1.20, got go%d.%d", major, minor)
Expand Down
6 changes: 5 additions & 1 deletion compiler/calls.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ const (
// createRuntimeCallCommon creates a runtime call. Use createRuntimeCall or
// createRuntimeInvoke instead.
func (b *builder) createRuntimeCallCommon(fnName string, args []llvm.Value, name string, isInvoke bool) llvm.Value {
fn := b.program.ImportedPackage("runtime").Members[fnName].(*ssa.Function)
member := b.program.ImportedPackage("runtime").Members[fnName]
if member == nil {
panic("unknown runtime call: " + fnName)
}
fn := member.(*ssa.Function)
fnType, llvmFn := b.getFunction(fn)
if llvmFn.IsNil() {
panic("trying to call non-existent function: " + fn.RelString(nil))
Expand Down
57 changes: 57 additions & 0 deletions compiler/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -1600,6 +1600,45 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
cplx = b.CreateInsertValue(cplx, r, 0, "")
cplx = b.CreateInsertValue(cplx, i, 1, "")
return cplx, nil
case "clear":
value := argValues[0]
switch typ := argTypes[0].Underlying().(type) {
case *types.Slice:
elementType := b.getLLVMType(typ.Elem())
elementSize := b.targetData.TypeAllocSize(elementType)
elementAlign := b.targetData.ABITypeAlignment(elementType)

// The pointer to the data to be cleared.
llvmBuf := b.CreateExtractValue(value, 0, "buf")
if llvmBuf.Type() != b.i8ptrType { // compatibility with LLVM 14
llvmBuf = b.CreateBitCast(llvmBuf, b.i8ptrType, "")
}

// The length (in bytes) to be cleared.
llvmLen := b.CreateExtractValue(value, 1, "len")
llvmLen = b.CreateMul(llvmLen, llvm.ConstInt(llvmLen.Type(), elementSize, false), "")

// Do the clear operation using the LLVM memset builtin.
// This is also correct for nil slices: in those cases, len will be
// 0 which means the memset call is a no-op (according to the LLVM
// LangRef).
memset := b.getMemsetFunc()
call := b.createCall(memset.GlobalValueType(), memset, []llvm.Value{
llvmBuf, // dest
llvm.ConstInt(b.ctx.Int8Type(), 0, false), // val
llvmLen, // len
llvm.ConstInt(b.ctx.Int1Type(), 0, false), // isVolatile
}, "")
call.AddCallSiteAttribute(1, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(elementAlign)))

return llvm.Value{}, nil
case *types.Map:
m := argValues[0]
b.createMapClear(m)
return llvm.Value{}, nil
default:
return llvm.Value{}, b.makeError(pos, "unsupported type in clear builtin: "+typ.String())
}
case "copy":
dst := argValues[0]
src := argValues[1]
Expand Down Expand Up @@ -1637,6 +1676,24 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
llvmLen = b.CreateZExt(llvmLen, b.intType, "len.int")
}
return llvmLen, nil
case "min", "max":
// min and max builtins, added in Go 1.21.
// We can simply reuse the existing binop comparison code, which has all
// the edge cases figured out already.
tok := token.LSS
if callName == "max" {
tok = token.GTR
}
result := argValues[0]
typ := argTypes[0]
for _, arg := range argValues[1:] {
cmp, err := b.createBinOp(tok, typ, typ, result, arg, pos)
if err != nil {
return result, err
}
result = b.CreateSelect(cmp, result, arg, "")
}
return result, nil
case "print", "println":
for i, value := range argValues {
if i >= 1 && callName == "println" {
Expand Down
3 changes: 3 additions & 0 deletions compiler/compiler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ func TestCompiler(t *testing.T) {
if goMinor >= 20 {
tests = append(tests, testCase{"go1.20.go", "", ""})
}
if goMinor >= 21 {
tests = append(tests, testCase{"go1.21.go", "", ""})
}

for _, tc := range tests {
name := tc.file
Expand Down
24 changes: 15 additions & 9 deletions compiler/intrinsics.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,7 @@ func (b *builder) createMemoryCopyImpl() {
// regular libc memset calls if they aren't optimized out in a different way.
func (b *builder) createMemoryZeroImpl() {
b.createFunctionStart(true)
fnName := "llvm.memset.p0.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
if llvmutil.Major() < 15 { // compatibility with LLVM 14
fnName = "llvm.memset.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
}
llvmFn := b.mod.NamedFunction(fnName)
if llvmFn.IsNil() {
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType, b.ctx.Int8Type(), b.uintptrType, b.ctx.Int1Type()}, false)
llvmFn = llvm.AddFunction(b.mod, fnName, fnType)
}
llvmFn := b.getMemsetFunc()
params := []llvm.Value{
b.getValue(b.fn.Params[0], getPos(b.fn)),
llvm.ConstInt(b.ctx.Int8Type(), 0, false),
Expand All @@ -89,6 +81,20 @@ func (b *builder) createMemoryZeroImpl() {
b.CreateRetVoid()
}

// Return the llvm.memset.p0.i8 function declaration.
func (c *compilerContext) getMemsetFunc() llvm.Value {
fnName := "llvm.memset.p0.i" + strconv.Itoa(c.uintptrType.IntTypeWidth())
if llvmutil.Major() < 15 { // compatibility with LLVM 14
fnName = "llvm.memset.p0i8.i" + strconv.Itoa(c.uintptrType.IntTypeWidth())
}
llvmFn := c.mod.NamedFunction(fnName)
if llvmFn.IsNil() {
fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.i8ptrType, c.ctx.Int8Type(), c.uintptrType, c.ctx.Int1Type()}, false)
llvmFn = llvm.AddFunction(c.mod, fnName, fnType)
}
return llvmFn
}

// createKeepAlive creates the runtime.KeepAlive function. It is implemented
// using inline assembly.
func (b *builder) createKeepAliveImpl() {
Expand Down
5 changes: 5 additions & 0 deletions compiler/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,11 @@ func (b *builder) createMapDelete(keyType types.Type, m, key llvm.Value, pos tok
}
}

// Clear the given map.
func (b *builder) createMapClear(m llvm.Value) {
b.createRuntimeCall("hashmapClear", []llvm.Value{m}, "")
}

// createMapIteratorNext lowers the *ssa.Next instruction for iterating over a
// map. It returns a tuple of {bool, key, value} with the result of the
// iteration.
Expand Down
2 changes: 1 addition & 1 deletion compiler/symbol.go
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
// The list of allowed types is based on this proposal:
// https://github.com/golang/go/issues/59149
func (c *compilerContext) checkWasmImport(f *ssa.Function, pragma string) {
if c.pkg.Path() == "runtime" {
if c.pkg.Path() == "runtime" || c.pkg.Path() == "syscall/js" {
// The runtime is a special case. Allow all kinds of parameters
// (importantly, including pointers).
return
Expand Down
65 changes: 65 additions & 0 deletions compiler/testdata/go1.21.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package main

func min1(a int) int {
return min(a)
}

func min2(a, b int) int {
return min(a, b)
}

func min3(a, b, c int) int {
return min(a, b, c)
}

func min4(a, b, c, d int) int {
return min(a, b, c, d)
}

func minUint8(a, b uint8) uint8 {
return min(a, b)
}

func minUnsigned(a, b uint) uint {
return min(a, b)
}

func minFloat32(a, b float32) float32 {
return min(a, b)
}

func minFloat64(a, b float64) float64 {
return min(a, b)
}

func minString(a, b string) string {
return min(a, b)
}

func maxInt(a, b int) int {
return max(a, b)
}

func maxUint(a, b uint) uint {
return max(a, b)
}

func maxFloat32(a, b float32) float32 {
return max(a, b)
}

func maxString(a, b string) string {
return max(a, b)
}

func clearSlice(s []int) {
clear(s)
}

func clearZeroSizedSlice(s []struct{}) {
clear(s)
}

func clearMap(m map[string]int) {
clear(m)
}
Loading
Loading