mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-04 11:07:46 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 98d55ab070 | |||
| 88d273da97 | |||
| adc0cae960 |
@@ -362,6 +362,7 @@ TEST_PACKAGES_FAST = \
|
|||||||
# debug/plan9obj requires os.ReadAt, which is not yet supported on windows
|
# debug/plan9obj requires os.ReadAt, which is not yet supported on windows
|
||||||
# image requires recover(), which is not yet supported on wasi
|
# image requires recover(), which is not yet supported on wasi
|
||||||
# io/ioutil requires os.ReadDir, which is not yet supported on windows or wasi
|
# io/ioutil requires os.ReadDir, which is not yet supported on windows or wasi
|
||||||
|
# math/bits: needs panic()/recover()
|
||||||
# mime: fail on wasi; neds panic()/recover()
|
# mime: fail on wasi; neds panic()/recover()
|
||||||
# mime/multipart: needs wasip1 syscall.FDFLAG_NONBLOCK
|
# mime/multipart: needs wasip1 syscall.FDFLAG_NONBLOCK
|
||||||
# mime/quotedprintable requires syscall.Faccessat
|
# mime/quotedprintable requires syscall.Faccessat
|
||||||
@@ -382,8 +383,11 @@ TEST_PACKAGES_LINUX := \
|
|||||||
crypto/hmac \
|
crypto/hmac \
|
||||||
debug/dwarf \
|
debug/dwarf \
|
||||||
debug/plan9obj \
|
debug/plan9obj \
|
||||||
|
encoding/binary \
|
||||||
|
go/constant \
|
||||||
image \
|
image \
|
||||||
io/ioutil \
|
io/ioutil \
|
||||||
|
math/bits \
|
||||||
mime \
|
mime \
|
||||||
mime/multipart \
|
mime/multipart \
|
||||||
mime/quotedprintable \
|
mime/quotedprintable \
|
||||||
@@ -403,6 +407,9 @@ TEST_PACKAGES_WINDOWS := \
|
|||||||
compress/flate \
|
compress/flate \
|
||||||
crypto/des \
|
crypto/des \
|
||||||
crypto/hmac \
|
crypto/hmac \
|
||||||
|
encoding/binary \
|
||||||
|
go/constant \
|
||||||
|
math/bits \
|
||||||
strconv \
|
strconv \
|
||||||
text/template/parse \
|
text/template/parse \
|
||||||
$(nil)
|
$(nil)
|
||||||
|
|||||||
+11
-11
@@ -31,7 +31,7 @@ func (b *builder) createLookupBoundsCheck(arrayLen, index llvm.Value) {
|
|||||||
|
|
||||||
// Now do the bounds check: index >= arrayLen
|
// Now do the bounds check: index >= arrayLen
|
||||||
outOfBounds := b.CreateICmp(llvm.IntUGE, index, arrayLen, "")
|
outOfBounds := b.CreateICmp(llvm.IntUGE, index, arrayLen, "")
|
||||||
b.createRuntimeAssert(outOfBounds, "lookup", "lookupPanic")
|
b.createRuntimeAssert(outOfBounds, "lookup", "lookupPanic", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// createSliceBoundsCheck emits a bounds check before a slicing operation to make
|
// createSliceBoundsCheck emits a bounds check before a slicing operation to make
|
||||||
@@ -74,7 +74,7 @@ func (b *builder) createSliceBoundsCheck(capacity, low, high, max llvm.Value, lo
|
|||||||
outOfBounds3 := b.CreateICmp(llvm.IntUGT, max, capacity, "slice.maxcap")
|
outOfBounds3 := b.CreateICmp(llvm.IntUGT, max, capacity, "slice.maxcap")
|
||||||
outOfBounds := b.CreateOr(outOfBounds1, outOfBounds2, "slice.lowmax")
|
outOfBounds := b.CreateOr(outOfBounds1, outOfBounds2, "slice.lowmax")
|
||||||
outOfBounds = b.CreateOr(outOfBounds, outOfBounds3, "slice.lowcap")
|
outOfBounds = b.CreateOr(outOfBounds, outOfBounds3, "slice.lowcap")
|
||||||
b.createRuntimeAssert(outOfBounds, "slice", "slicePanic")
|
b.createRuntimeAssert(outOfBounds, "slice", "slicePanic", false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// createSliceToArrayPointerCheck adds a check for slice-to-array pointer
|
// createSliceToArrayPointerCheck adds a check for slice-to-array pointer
|
||||||
@@ -86,7 +86,7 @@ func (b *builder) createSliceToArrayPointerCheck(sliceLen llvm.Value, arrayLen i
|
|||||||
// > run-time panic occurs.
|
// > run-time panic occurs.
|
||||||
arrayLenValue := llvm.ConstInt(b.uintptrType, uint64(arrayLen), false)
|
arrayLenValue := llvm.ConstInt(b.uintptrType, uint64(arrayLen), false)
|
||||||
isLess := b.CreateICmp(llvm.IntULT, sliceLen, arrayLenValue, "")
|
isLess := b.CreateICmp(llvm.IntULT, sliceLen, arrayLenValue, "")
|
||||||
b.createRuntimeAssert(isLess, "slicetoarray", "sliceToArrayPointerPanic")
|
b.createRuntimeAssert(isLess, "slicetoarray", "sliceToArrayPointerPanic", false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// createUnsafeSliceStringCheck inserts a runtime check used for unsafe.Slice
|
// createUnsafeSliceStringCheck inserts a runtime check used for unsafe.Slice
|
||||||
@@ -118,7 +118,7 @@ func (b *builder) createUnsafeSliceStringCheck(name string, ptr, len llvm.Value,
|
|||||||
lenIsNotZero := b.CreateICmp(llvm.IntNE, len, zero, "")
|
lenIsNotZero := b.CreateICmp(llvm.IntNE, len, zero, "")
|
||||||
assert := b.CreateAnd(ptrIsNil, lenIsNotZero, "")
|
assert := b.CreateAnd(ptrIsNil, lenIsNotZero, "")
|
||||||
assert = b.CreateOr(assert, lenOutOfBounds, "")
|
assert = b.CreateOr(assert, lenOutOfBounds, "")
|
||||||
b.createRuntimeAssert(assert, name, "unsafeSlicePanic")
|
b.createRuntimeAssert(assert, name, "unsafeSlicePanic", false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// createChanBoundsCheck creates a bounds check before creating a new channel to
|
// createChanBoundsCheck creates a bounds check before creating a new channel to
|
||||||
@@ -155,7 +155,7 @@ func (b *builder) createChanBoundsCheck(elementSize uint64, bufSize llvm.Value,
|
|||||||
|
|
||||||
// Do the check for a too large (or negative) buffer size.
|
// Do the check for a too large (or negative) buffer size.
|
||||||
bufSizeTooBig := b.CreateICmp(llvm.IntUGE, bufSize, maxBufSize, "")
|
bufSizeTooBig := b.CreateICmp(llvm.IntUGE, bufSize, maxBufSize, "")
|
||||||
b.createRuntimeAssert(bufSizeTooBig, "chan", "chanMakePanic")
|
b.createRuntimeAssert(bufSizeTooBig, "chan", "chanMakePanic", false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// createNilCheck checks whether the given pointer is nil, and panics if it is.
|
// createNilCheck checks whether the given pointer is nil, and panics if it is.
|
||||||
@@ -199,7 +199,7 @@ func (b *builder) createNilCheck(inst ssa.Value, ptr llvm.Value, blockPrefix str
|
|||||||
isnil := b.CreateICmp(llvm.IntEQ, ptr, nilptr, "")
|
isnil := b.CreateICmp(llvm.IntEQ, ptr, nilptr, "")
|
||||||
|
|
||||||
// Emit the nil check in IR.
|
// Emit the nil check in IR.
|
||||||
b.createRuntimeAssert(isnil, blockPrefix, "nilPanic")
|
b.createRuntimeAssert(isnil, blockPrefix, "nilPanic", false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// createNegativeShiftCheck creates an assertion that panics if the given shift value is negative.
|
// createNegativeShiftCheck creates an assertion that panics if the given shift value is negative.
|
||||||
@@ -212,7 +212,7 @@ func (b *builder) createNegativeShiftCheck(shift llvm.Value) {
|
|||||||
|
|
||||||
// isNegative = shift < 0
|
// isNegative = shift < 0
|
||||||
isNegative := b.CreateICmp(llvm.IntSLT, shift, llvm.ConstInt(shift.Type(), 0, false), "")
|
isNegative := b.CreateICmp(llvm.IntSLT, shift, llvm.ConstInt(shift.Type(), 0, false), "")
|
||||||
b.createRuntimeAssert(isNegative, "shift", "negativeShiftPanic")
|
b.createRuntimeAssert(isNegative, "shift", "negativeShiftPanic", false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// createDivideByZeroCheck asserts that y is not zero. If it is, a runtime panic
|
// createDivideByZeroCheck asserts that y is not zero. If it is, a runtime panic
|
||||||
@@ -225,12 +225,12 @@ func (b *builder) createDivideByZeroCheck(y llvm.Value) {
|
|||||||
|
|
||||||
// isZero = y == 0
|
// isZero = y == 0
|
||||||
isZero := b.CreateICmp(llvm.IntEQ, y, llvm.ConstInt(y.Type(), 0, false), "")
|
isZero := b.CreateICmp(llvm.IntEQ, y, llvm.ConstInt(y.Type(), 0, false), "")
|
||||||
b.createRuntimeAssert(isZero, "divbyzero", "divideByZeroPanic")
|
b.createRuntimeAssert(isZero, "divbyzero", "divideByZeroPanic", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// createRuntimeAssert is a common function to create a new branch on an assert
|
// createRuntimeAssert is a common function to create a new branch on an assert
|
||||||
// bool, calling an assert func if the assert value is true (1).
|
// bool, calling an assert func if the assert value is true (1).
|
||||||
func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc string) {
|
func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc string, isInvoke bool) {
|
||||||
// Check whether we can resolve this check at compile time.
|
// Check whether we can resolve this check at compile time.
|
||||||
if !assert.IsAConstantInt().IsNil() {
|
if !assert.IsAConstantInt().IsNil() {
|
||||||
val := assert.ZExtValue()
|
val := assert.ZExtValue()
|
||||||
@@ -245,17 +245,17 @@ func (b *builder) createRuntimeAssert(assert llvm.Value, blockPrefix, assertFunc
|
|||||||
// current insert position.
|
// current insert position.
|
||||||
faultBlock := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".throw")
|
faultBlock := b.ctx.AddBasicBlock(b.llvmFn, blockPrefix+".throw")
|
||||||
nextBlock := b.insertBasicBlock(blockPrefix + ".next")
|
nextBlock := b.insertBasicBlock(blockPrefix + ".next")
|
||||||
b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes
|
|
||||||
|
|
||||||
// Now branch to the out-of-bounds or the regular block.
|
// Now branch to the out-of-bounds or the regular block.
|
||||||
b.CreateCondBr(assert, faultBlock, nextBlock)
|
b.CreateCondBr(assert, faultBlock, nextBlock)
|
||||||
|
|
||||||
// Fail: the assert triggered so panic.
|
// Fail: the assert triggered so panic.
|
||||||
b.SetInsertPointAtEnd(faultBlock)
|
b.SetInsertPointAtEnd(faultBlock)
|
||||||
b.createRuntimeCall(assertFunc, nil, "")
|
b.createRuntimeCallCommon(assertFunc, nil, "", isInvoke)
|
||||||
b.CreateUnreachable()
|
b.CreateUnreachable()
|
||||||
|
|
||||||
// Ok: assert didn't trigger so continue normally.
|
// Ok: assert didn't trigger so continue normally.
|
||||||
|
b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes
|
||||||
b.SetInsertPointAtEnd(nextBlock)
|
b.SetInsertPointAtEnd(nextBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
-2
@@ -688,20 +688,25 @@ func (c *compilerContext) getGlobalInfo(g *ssa.Global) globalInfo {
|
|||||||
// Check for //go: pragmas, which may change the link name (among others).
|
// Check for //go: pragmas, which may change the link name (among others).
|
||||||
doc := c.astComments[info.linkName]
|
doc := c.astComments[info.linkName]
|
||||||
if doc != nil {
|
if doc != nil {
|
||||||
info.parsePragmas(doc)
|
info.parsePragmas(doc, g)
|
||||||
}
|
}
|
||||||
return info
|
return info
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse //go: pragma comments from the source. In particular, it parses the
|
// Parse //go: pragma comments from the source. In particular, it parses the
|
||||||
// //go:extern pragma on globals.
|
// //go:extern pragma on globals.
|
||||||
func (info *globalInfo) parsePragmas(doc *ast.CommentGroup) {
|
func (info *globalInfo) parsePragmas(doc *ast.CommentGroup, g *ssa.Global) {
|
||||||
for _, comment := range doc.List {
|
for _, comment := range doc.List {
|
||||||
if !strings.HasPrefix(comment.Text, "//go:") {
|
if !strings.HasPrefix(comment.Text, "//go:") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
parts := strings.Fields(comment.Text)
|
parts := strings.Fields(comment.Text)
|
||||||
switch parts[0] {
|
switch parts[0] {
|
||||||
|
case "//go:linkname":
|
||||||
|
if len(parts) == 3 && g.Name() == parts[1] {
|
||||||
|
info.linkName = parts[2]
|
||||||
|
info.extern = true
|
||||||
|
}
|
||||||
case "//go:extern":
|
case "//go:extern":
|
||||||
info.extern = true
|
info.extern = true
|
||||||
if len(parts) == 2 {
|
if len(parts) == 2 {
|
||||||
|
|||||||
Vendored
+6
@@ -2,6 +2,12 @@ package main
|
|||||||
|
|
||||||
import _ "unsafe"
|
import _ "unsafe"
|
||||||
|
|
||||||
|
// Use the go:linkname mechanism to link this global to a different package.
|
||||||
|
// This is used in math/bits.
|
||||||
|
//
|
||||||
|
//go:linkname linknamedGlobal runtime.testLinknamedGlobal
|
||||||
|
var linknamedGlobal int
|
||||||
|
|
||||||
// Creates an external global with name extern_global.
|
// Creates an external global with name extern_global.
|
||||||
//
|
//
|
||||||
//go:extern extern_global
|
//go:extern extern_global
|
||||||
|
|||||||
Vendored
+1
@@ -3,6 +3,7 @@ source_filename = "pragma.go"
|
|||||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
|
@runtime.testLinknamedGlobal = external global i32, align 4
|
||||||
@extern_global = external global [0 x i8], align 1
|
@extern_global = external global [0 x i8], align 1
|
||||||
@main.alignedGlobal = hidden global [4 x i32] zeroinitializer, align 32
|
@main.alignedGlobal = hidden global [4 x i32] zeroinitializer, align 32
|
||||||
@main.alignedGlobal16 = hidden global [4 x i32] zeroinitializer, align 16
|
@main.alignedGlobal16 = hidden global [4 x i32] zeroinitializer, align 16
|
||||||
|
|||||||
@@ -4,5 +4,23 @@ package runtime
|
|||||||
type Error interface {
|
type Error interface {
|
||||||
error
|
error
|
||||||
|
|
||||||
|
// Method to indicate this is indeed a runtime error.
|
||||||
RuntimeError()
|
RuntimeError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type runtimeError struct {
|
||||||
|
msg string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r runtimeError) Error() string {
|
||||||
|
return r.msg
|
||||||
|
}
|
||||||
|
|
||||||
|
// Purely here to satisfy the Error interface.
|
||||||
|
func (r runtimeError) RuntimeError() {}
|
||||||
|
|
||||||
|
var (
|
||||||
|
divideError error = runtimeError{"runtime error: integer divide by zero"}
|
||||||
|
lookupError error = runtimeError{"runtime error: index out of range"}
|
||||||
|
overflowError error = runtimeError{"runtime error: integer overflow"}
|
||||||
|
)
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ func nilMapPanic() {
|
|||||||
|
|
||||||
// Panic when trying to access an array or slice out of bounds.
|
// Panic when trying to access an array or slice out of bounds.
|
||||||
func lookupPanic() {
|
func lookupPanic() {
|
||||||
runtimePanicAt(returnAddress(0), "index out of range")
|
_panic(lookupError)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Panic when trying to slice a slice out of bounds.
|
// Panic when trying to slice a slice out of bounds.
|
||||||
@@ -220,7 +220,7 @@ func negativeShiftPanic() {
|
|||||||
|
|
||||||
// Panic when there is a divide by zero.
|
// Panic when there is a divide by zero.
|
||||||
func divideByZeroPanic() {
|
func divideByZeroPanic() {
|
||||||
runtimePanicAt(returnAddress(0), "divide by zero")
|
_panic(divideError)
|
||||||
}
|
}
|
||||||
|
|
||||||
func blockingPanic() {
|
func blockingPanic() {
|
||||||
|
|||||||
Vendored
+24
@@ -30,6 +30,10 @@ func main() {
|
|||||||
println("\n# defer panic")
|
println("\n# defer panic")
|
||||||
deferPanic()
|
deferPanic()
|
||||||
|
|
||||||
|
println("\n# runtime panics")
|
||||||
|
runtimePanicDivByZero(1, 0)
|
||||||
|
runtimePanicLookup([]int{1, 2, 3}, 10)
|
||||||
|
|
||||||
println("\n# runtime.Goexit")
|
println("\n# runtime.Goexit")
|
||||||
runtimeGoexit()
|
runtimeGoexit()
|
||||||
}
|
}
|
||||||
@@ -114,6 +118,26 @@ func deferPanic() {
|
|||||||
println("defer panic")
|
println("defer panic")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runtimePanicDivByZero(a, b int) int {
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
println("recovered:", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return a / b
|
||||||
|
}
|
||||||
|
|
||||||
|
func runtimePanicLookup(slice []int, index int) int {
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
println("recovered:", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return slice[index]
|
||||||
|
}
|
||||||
|
|
||||||
func runtimeGoexit() {
|
func runtimeGoexit() {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
|||||||
Reference in New Issue
Block a user