diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2016-10-12 18:17:52 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2016-10-12 18:17:52 +0000 |
commit | 1ad16c52842e0513b96a0e02d2a431dc0f338c5d (patch) | |
tree | f6eaddf65fba9d031ed9b3008a3c3ba4ff0ebd0b /libgo/go | |
parent | 2ec69f566076547b618447ba5531260c25abed3e (diff) | |
download | gcc-1ad16c52842e0513b96a0e02d2a431dc0f338c5d.zip gcc-1ad16c52842e0513b96a0e02d2a431dc0f338c5d.tar.gz gcc-1ad16c52842e0513b96a0e02d2a431dc0f338c5d.tar.bz2 |
compiler, runtime: copy string code from Go 1.7
Add compiler support for turning concatenating strings into a call to
a runtime function that takes the appropriate number of arguments.
Rename some local variables in mgc0.c to avoid macros that the new
rune.go causes to appear in runtime.inc.
Reviewed-on: https://go-review.googlesource.com/30827
From-SVN: r241074
Diffstat (limited to 'libgo/go')
-rw-r--r-- | libgo/go/runtime/error.go | 6 | ||||
-rw-r--r-- | libgo/go/runtime/rune.go | 219 | ||||
-rw-r--r-- | libgo/go/runtime/string.go | 446 | ||||
-rw-r--r-- | libgo/go/runtime/string_test.go | 4 | ||||
-rw-r--r-- | libgo/go/runtime/stubs.go | 84 |
5 files changed, 674 insertions, 85 deletions
diff --git a/libgo/go/runtime/error.go b/libgo/go/runtime/error.go index b9c7365..3683001 100644 --- a/libgo/go/runtime/error.go +++ b/libgo/go/runtime/error.go @@ -4,6 +4,8 @@ package runtime +import "unsafe" + // The Error interface identifies a run time error. type Error interface { error @@ -107,10 +109,8 @@ type errorCString struct{ cstr uintptr } func (e errorCString) RuntimeError() {} -func cstringToGo(uintptr) string - func (e errorCString) Error() string { - return "runtime error: " + cstringToGo(e.cstr) + return "runtime error: " + gostringnocopy((*byte)(unsafe.Pointer(e.cstr))) } // For calling from C. diff --git a/libgo/go/runtime/rune.go b/libgo/go/runtime/rune.go new file mode 100644 index 0000000..99c38e0 --- /dev/null +++ b/libgo/go/runtime/rune.go @@ -0,0 +1,219 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Portions Copyright 2009 The Go Authors. All rights reserved. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ + +/* + * This code is copied, with slight editing due to type differences, + * from a subset of ../lib9/utf/rune.c [which no longer exists] + */ + +package runtime + +const ( + bit1 = 7 + bitx = 6 + bit2 = 5 + bit3 = 4 + bit4 = 3 + bit5 = 2 + + t1 = ((1 << (bit1 + 1)) - 1) ^ 0xFF /* 0000 0000 */ + tx = ((1 << (bitx + 1)) - 1) ^ 0xFF /* 1000 0000 */ + t2 = ((1 << (bit2 + 1)) - 1) ^ 0xFF /* 1100 0000 */ + t3 = ((1 << (bit3 + 1)) - 1) ^ 0xFF /* 1110 0000 */ + t4 = ((1 << (bit4 + 1)) - 1) ^ 0xFF /* 1111 0000 */ + t5 = ((1 << (bit5 + 1)) - 1) ^ 0xFF /* 1111 1000 */ + + rune1 = (1 << (bit1 + 0*bitx)) - 1 /* 0000 0000 0111 1111 */ + rune2 = (1 << (bit2 + 1*bitx)) - 1 /* 0000 0111 1111 1111 */ + rune3 = (1 << (bit3 + 2*bitx)) - 1 /* 1111 1111 1111 1111 */ + rune4 = (1 << (bit4 + 3*bitx)) - 1 /* 0001 1111 1111 1111 1111 1111 */ + + maskx = (1 << bitx) - 1 /* 0011 1111 */ + testx = maskx ^ 0xFF /* 1100 0000 */ + + runeerror = 0xFFFD + runeself = 0x80 + + surrogateMin = 0xD800 + surrogateMax = 0xDFFF + + bad = runeerror + + runemax = 0x10FFFF /* maximum rune value */ +) + +/* + * Modified by Wei-Hwa Huang, Google Inc., on 2004-09-24 + * This is a slower but "safe" version of the old chartorune + * that works on strings that are not necessarily null-terminated. + * + * If you know for sure that your string is null-terminated, + * chartorune will be a bit faster. + * + * It is guaranteed not to attempt to access "length" + * past the incoming pointer. This is to avoid + * possible access violations. If the string appears to be + * well-formed but incomplete (i.e., to get the whole Rune + * we'd need to read past str+length) then we'll set the Rune + * to Bad and return 0. + * + * Note that if we have decoding problems for other + * reasons, we return 1 instead of 0. + */ +func charntorune(s string) (rune, int) { + /* When we're not allowed to read anything */ + if len(s) <= 0 { + return bad, 1 + } + + /* + * one character sequence (7-bit value) + * 00000-0007F => T1 + */ + c := s[0] + if c < tx { + return rune(c), 1 + } + + // If we can't read more than one character we must stop + if len(s) <= 1 { + return bad, 1 + } + + /* + * two character sequence (11-bit value) + * 0080-07FF => t2 tx + */ + c1 := s[1] ^ tx + if (c1 & testx) != 0 { + return bad, 1 + } + if c < t3 { + if c < t2 { + return bad, 1 + } + l := ((rune(c) << bitx) | rune(c1)) & rune2 + if l <= rune1 { + return bad, 1 + } + return l, 2 + } + + // If we can't read more than two characters we must stop + if len(s) <= 2 { + return bad, 1 + } + + /* + * three character sequence (16-bit value) + * 0800-FFFF => t3 tx tx + */ + c2 := s[2] ^ tx + if (c2 & testx) != 0 { + return bad, 1 + } + if c < t4 { + l := ((((rune(c) << bitx) | rune(c1)) << bitx) | rune(c2)) & rune3 + if l <= rune2 { + return bad, 1 + } + if surrogateMin <= l && l <= surrogateMax { + return bad, 1 + } + return l, 3 + } + + if len(s) <= 3 { + return bad, 1 + } + + /* + * four character sequence (21-bit value) + * 10000-1FFFFF => t4 tx tx tx + */ + c3 := s[3] ^ tx + if (c3 & testx) != 0 { + return bad, 1 + } + if c < t5 { + l := ((((((rune(c) << bitx) | rune(c1)) << bitx) | rune(c2)) << bitx) | rune(c3)) & rune4 + if l <= rune3 || l > runemax { + return bad, 1 + } + return l, 4 + } + + // Support for 5-byte or longer UTF-8 would go here, but + // since we don't have that, we'll just return bad. + return bad, 1 +} + +// runetochar converts r to bytes and writes the result to str. +// returns the number of bytes generated. +func runetochar(str []byte, r rune) int { + /* runes are signed, so convert to unsigned for range check. */ + c := uint32(r) + /* + * one character sequence + * 00000-0007F => 00-7F + */ + if c <= rune1 { + str[0] = byte(c) + return 1 + } + /* + * two character sequence + * 0080-07FF => t2 tx + */ + if c <= rune2 { + str[0] = byte(t2 | (c >> (1 * bitx))) + str[1] = byte(tx | (c & maskx)) + return 2 + } + + /* + * If the rune is out of range or a surrogate half, convert it to the error rune. + * Do this test here because the error rune encodes to three bytes. + * Doing it earlier would duplicate work, since an out of range + * rune wouldn't have fit in one or two bytes. + */ + if c > runemax { + c = runeerror + } + if surrogateMin <= c && c <= surrogateMax { + c = runeerror + } + + /* + * three character sequence + * 0800-FFFF => t3 tx tx + */ + if c <= rune3 { + str[0] = byte(t3 | (c >> (2 * bitx))) + str[1] = byte(tx | ((c >> (1 * bitx)) & maskx)) + str[2] = byte(tx | (c & maskx)) + return 3 + } + + /* + * four character sequence (21-bit value) + * 10000-1FFFFF => t4 tx tx tx + */ + str[0] = byte(t4 | (c >> (3 * bitx))) + str[1] = byte(tx | ((c >> (2 * bitx)) & maskx)) + str[2] = byte(tx | ((c >> (1 * bitx)) & maskx)) + str[3] = byte(tx | (c & maskx)) + return 4 +} diff --git a/libgo/go/runtime/string.go b/libgo/go/runtime/string.go new file mode 100644 index 0000000..5df3aa3 --- /dev/null +++ b/libgo/go/runtime/string.go @@ -0,0 +1,446 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runtime + +import ( + "unsafe" +) + +// For gccgo, use go:linkname to rename compiler-called functions to +// themselves, so that the compiler will export them. +// +//go:linkname concatstrings runtime.concatstrings +//go:linkname concatstring2 runtime.concatstring2 +//go:linkname concatstring3 runtime.concatstring3 +//go:linkname concatstring4 runtime.concatstring4 +//go:linkname concatstring5 runtime.concatstring5 +//go:linkname slicebytetostring runtime.slicebytetostring +//go:linkname slicebytetostringtmp runtime.slicebytetostringtmp +//go:linkname stringtoslicebyte runtime.stringtoslicebyte +//go:linkname stringtoslicebytetmp runtime.stringtoslicebytetmp +//go:linkname stringtoslicerune runtime.stringtoslicerune +//go:linkname slicerunetostring runtime.slicerunetostring +//go:linkname intstring runtime.intstring +//go:linkname stringiter runtime.stringiter +//go:linkname stringiter2 runtime.stringiter2 +// Temporary for C code to call: +//go:linkname gostringnocopy runtime.gostringnocopy +//go:linkname findnull runtime.findnull + +// The constant is known to the compiler. +// There is no fundamental theory behind this number. +const tmpStringBufSize = 32 + +type tmpBuf [tmpStringBufSize]byte + +// concatstrings implements a Go string concatenation x+y+z+... +// The operands are passed in the slice a. +// If buf != nil, the compiler has determined that the result does not +// escape the calling function, so the string data can be stored in buf +// if small enough. +func concatstrings(buf *tmpBuf, a []string) string { + // idx := 0 + l := 0 + count := 0 + for _, x := range a { + n := len(x) + if n == 0 { + continue + } + if l+n < l { + throw("string concatenation too long") + } + l += n + count++ + // idx = i + } + if count == 0 { + return "" + } + + // If there is just one string and either it is not on the stack + // or our result does not escape the calling frame (buf != nil), + // then we can return that string directly. + // Commented out for gccgo--no implementation of stringDataOnStack. + // if count == 1 && (buf != nil || !stringDataOnStack(a[idx])) { + // return a[idx] + // } + s, b := rawstringtmp(buf, l) + l = 0 + for _, x := range a { + copy(b[l:], x) + l += len(x) + } + return s +} + +func concatstring2(buf *tmpBuf, a [2]string) string { + return concatstrings(buf, a[:]) +} + +func concatstring3(buf *tmpBuf, a [3]string) string { + return concatstrings(buf, a[:]) +} + +func concatstring4(buf *tmpBuf, a [4]string) string { + return concatstrings(buf, a[:]) +} + +func concatstring5(buf *tmpBuf, a [5]string) string { + return concatstrings(buf, a[:]) +} + +// Buf is a fixed-size buffer for the result, +// it is not nil if the result does not escape. +func slicebytetostring(buf *tmpBuf, b []byte) string { + l := len(b) + if l == 0 { + // Turns out to be a relatively common case. + // Consider that you want to parse out data between parens in "foo()bar", + // you find the indices and convert the subslice to string. + return "" + } + if raceenabled && l > 0 { + racereadrangepc(unsafe.Pointer(&b[0]), + uintptr(l), + getcallerpc(unsafe.Pointer(&buf)), + funcPC(slicebytetostring)) + } + if msanenabled && l > 0 { + msanread(unsafe.Pointer(&b[0]), uintptr(l)) + } + s, c := rawstringtmp(buf, l) + copy(c, b) + return s +} + +func rawstringtmp(buf *tmpBuf, l int) (s string, b []byte) { + if buf != nil && l <= len(buf) { + b = buf[:l] + s = slicebytetostringtmp(b) + } else { + s, b = rawstring(l) + } + return +} + +func slicebytetostringtmp(b []byte) string { + // Return a "string" referring to the actual []byte bytes. + // This is only for use by internal compiler optimizations + // that know that the string form will be discarded before + // the calling goroutine could possibly modify the original + // slice or synchronize with another goroutine. + // First such case is a m[string(k)] lookup where + // m is a string-keyed map and k is a []byte. + // Second such case is "<"+string(b)+">" concatenation where b is []byte. + // Third such case is string(b)=="foo" comparison where b is []byte. + + if raceenabled && len(b) > 0 { + racereadrangepc(unsafe.Pointer(&b[0]), + uintptr(len(b)), + getcallerpc(unsafe.Pointer(&b)), + funcPC(slicebytetostringtmp)) + } + if msanenabled && len(b) > 0 { + msanread(unsafe.Pointer(&b[0]), uintptr(len(b))) + } + return *(*string)(unsafe.Pointer(&b)) +} + +func stringtoslicebyte(buf *tmpBuf, s string) []byte { + var b []byte + if buf != nil && len(s) <= len(buf) { + *buf = tmpBuf{} + b = buf[:len(s)] + } else { + b = rawbyteslice(len(s)) + } + copy(b, s) + return b +} + +func stringtoslicebytetmp(s string) []byte { + // Return a slice referring to the actual string bytes. + // This is only for use by internal compiler optimizations + // that know that the slice won't be mutated. + // The only such case today is: + // for i, c := range []byte(str) + + str := stringStructOf(&s) + ret := slice{array: str.str, len: str.len, cap: str.len} + return *(*[]byte)(unsafe.Pointer(&ret)) +} + +func stringtoslicerune(buf *[tmpStringBufSize]rune, s string) []rune { + // two passes. + // unlike slicerunetostring, no race because strings are immutable. + n := 0 + t := s + for len(s) > 0 { + _, k := charntorune(s) + s = s[k:] + n++ + } + var a []rune + if buf != nil && n <= len(buf) { + *buf = [tmpStringBufSize]rune{} + a = buf[:n] + } else { + a = rawruneslice(n) + } + n = 0 + for len(t) > 0 { + r, k := charntorune(t) + t = t[k:] + a[n] = r + n++ + } + return a +} + +func slicerunetostring(buf *tmpBuf, a []rune) string { + if raceenabled && len(a) > 0 { + racereadrangepc(unsafe.Pointer(&a[0]), + uintptr(len(a))*unsafe.Sizeof(a[0]), + getcallerpc(unsafe.Pointer(&buf)), + funcPC(slicerunetostring)) + } + if msanenabled && len(a) > 0 { + msanread(unsafe.Pointer(&a[0]), uintptr(len(a))*unsafe.Sizeof(a[0])) + } + var dum [4]byte + size1 := 0 + for _, r := range a { + size1 += runetochar(dum[:], r) + } + s, b := rawstringtmp(buf, size1+3) + size2 := 0 + for _, r := range a { + // check for race + if size2 >= size1 { + break + } + size2 += runetochar(b[size2:], r) + } + return s[:size2] +} + +type stringStruct struct { + str unsafe.Pointer + len int +} + +// Variant with *byte pointer type for DWARF debugging. +type stringStructDWARF struct { + str *byte + len int +} + +func stringStructOf(sp *string) *stringStruct { + return (*stringStruct)(unsafe.Pointer(sp)) +} + +func intstring(buf *[4]byte, v int64) string { + var s string + var b []byte + if buf != nil { + b = buf[:] + s = slicebytetostringtmp(b) + } else { + s, b = rawstring(4) + } + if int64(rune(v)) != v { + v = runeerror + } + n := runetochar(b, rune(v)) + return s[:n] +} + +// stringiter returns the index of the next +// rune after the rune that starts at s[k]. +func stringiter(s string, k int) int { + if k >= len(s) { + // 0 is end of iteration + return 0 + } + + c := s[k] + if c < runeself { + return k + 1 + } + + // multi-char rune + _, n := charntorune(s[k:]) + return k + n +} + +// stringiter2 returns the rune that starts at s[k] +// and the index where the next rune starts. +func stringiter2(s string, k int) (int, rune) { + if k >= len(s) { + // 0 is end of iteration + return 0, 0 + } + + c := s[k] + if c < runeself { + return k + 1, rune(c) + } + + // multi-char rune + r, n := charntorune(s[k:]) + return k + n, r +} + +// rawstring allocates storage for a new string. The returned +// string and byte slice both refer to the same storage. +// The storage is not zeroed. Callers should use +// b to set the string contents and then drop b. +func rawstring(size int) (s string, b []byte) { + p := mallocgc(uintptr(size), nil, false) + + stringStructOf(&s).str = p + stringStructOf(&s).len = size + + *(*slice)(unsafe.Pointer(&b)) = slice{p, size, size} + return +} + +// rawbyteslice allocates a new byte slice. The byte slice is not zeroed. +func rawbyteslice(size int) (b []byte) { + cap := roundupsize(uintptr(size)) + p := mallocgc(cap, nil, false) + if cap != uintptr(size) { + memclr(add(p, uintptr(size)), cap-uintptr(size)) + } + + *(*slice)(unsafe.Pointer(&b)) = slice{p, size, int(cap)} + return +} + +// rawruneslice allocates a new rune slice. The rune slice is not zeroed. +func rawruneslice(size int) (b []rune) { + if uintptr(size) > _MaxMem/4 { + throw("out of memory") + } + mem := roundupsize(uintptr(size) * 4) + p := mallocgc(mem, nil, false) + if mem != uintptr(size)*4 { + memclr(add(p, uintptr(size)*4), mem-uintptr(size)*4) + } + + *(*slice)(unsafe.Pointer(&b)) = slice{p, size, int(mem / 4)} + return +} + +// used by cmd/cgo +func gobytes(p *byte, n int) []byte { + if n == 0 { + return make([]byte, 0) + } + x := make([]byte, n) + memmove(unsafe.Pointer(&x[0]), unsafe.Pointer(p), uintptr(n)) + return x +} + +func gostring(p *byte) string { + l := findnull(p) + if l == 0 { + return "" + } + s, b := rawstring(l) + memmove(unsafe.Pointer(&b[0]), unsafe.Pointer(p), uintptr(l)) + return s +} + +func gostringn(p *byte, l int) string { + if l == 0 { + return "" + } + s, b := rawstring(l) + memmove(unsafe.Pointer(&b[0]), unsafe.Pointer(p), uintptr(l)) + return s +} + +func index(s, t string) int { + if len(t) == 0 { + return 0 + } + for i := 0; i < len(s); i++ { + if s[i] == t[0] && hasprefix(s[i:], t) { + return i + } + } + return -1 +} + +func contains(s, t string) bool { + return index(s, t) >= 0 +} + +func hasprefix(s, t string) bool { + return len(s) >= len(t) && s[:len(t)] == t +} + +func atoi(s string) int { + n := 0 + for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { + n = n*10 + int(s[0]) - '0' + s = s[1:] + } + return n +} + +//go:nosplit +func findnull(s *byte) int { + if s == nil { + return 0 + } + p := (*[_MaxMem/2 - 1]byte)(unsafe.Pointer(s)) + l := 0 + for p[l] != 0 { + l++ + } + return l +} + +func findnullw(s *uint16) int { + if s == nil { + return 0 + } + p := (*[_MaxMem/2/2 - 1]uint16)(unsafe.Pointer(s)) + l := 0 + for p[l] != 0 { + l++ + } + return l +} + +//go:nosplit +func gostringnocopy(str *byte) string { + ss := stringStruct{str: unsafe.Pointer(str), len: findnull(str)} + s := *(*string)(unsafe.Pointer(&ss)) + return s +} + +func gostringw(strw *uint16) string { + var buf [8]byte + str := (*[_MaxMem/2/2 - 1]uint16)(unsafe.Pointer(strw)) + n1 := 0 + for i := 0; str[i] != 0; i++ { + n1 += runetochar(buf[:], rune(str[i])) + } + s, b := rawstring(n1 + 4) + n2 := 0 + for i := 0; str[i] != 0; i++ { + // check for race + if n2 >= n1 { + break + } + n2 += runetochar(b[n2:], rune(str[i])) + } + b[n2] = 0 // for luck + return s[:n2] +} diff --git a/libgo/go/runtime/string_test.go b/libgo/go/runtime/string_test.go index f9411c0..11fa454 100644 --- a/libgo/go/runtime/string_test.go +++ b/libgo/go/runtime/string_test.go @@ -223,7 +223,9 @@ func TestIntStringAllocs(t *testing.T) { t.Fatalf("bad") } }) - if n != 0 { + // was n != 0, changed for gccgo, which currently does one + // allocation for each call to string(unknown). + if n > 2 { t.Fatalf("want 0 allocs, got %v", n) } } diff --git a/libgo/go/runtime/stubs.go b/libgo/go/runtime/stubs.go index f014610..d598a0a 100644 --- a/libgo/go/runtime/stubs.go +++ b/libgo/go/runtime/stubs.go @@ -250,17 +250,6 @@ func typedmemmove(typ *_type, dst, src unsafe.Pointer) { memmove(dst, src, typ.size) } -// Here for gccgo unless and until we port string.go. -type stringStruct struct { - str unsafe.Pointer - len int -} - -// Here for gccgo unless and until we port string.go. -func stringStructOf(sp *string) *stringStruct { - return (*stringStruct)(unsafe.Pointer(sp)) -} - // Here for gccgo unless and until we port slice.go. type slice struct { array unsafe.Pointer @@ -286,76 +275,6 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer { return c_mallocgc(size, uintptr(unsafe.Pointer(typ)), flag) } -// Here for gccgo unless and until we port string.go. -func rawstring(size int) (p unsafe.Pointer, s string) { - p = mallocgc(uintptr(size), nil, false) - - (*(*stringStruct)(unsafe.Pointer(&s))).str = p - (*(*stringStruct)(unsafe.Pointer(&s))).len = size - - return -} - -// Here for gccgo unless and until we port string.go. -func gostring(p *byte) string { - l := findnull(p) - if l == 0 { - return "" - } - m, s := rawstring(l) - memmove(m, unsafe.Pointer(p), uintptr(l)) - return s -} - -// Here for gccgo unless and until we port string.go. -func index(s, t string) int { - if len(t) == 0 { - return 0 - } - for i := 0; i < len(s); i++ { - if s[i] == t[0] && hasprefix(s[i:], t) { - return i - } - } - return -1 -} - -// Here for gccgo unless and until we port string.go. -func hasprefix(s, t string) bool { - return len(s) >= len(t) && s[:len(t)] == t -} - -// Here for gccgo unless and until we port string.go. -//go:nosplit -func findnull(s *byte) int { - if s == nil { - return 0 - } - p := (*[_MaxMem/2 - 1]byte)(unsafe.Pointer(s)) - l := 0 - for p[l] != 0 { - l++ - } - return l -} - -// Here for gccgo unless and until we port string.go. -//go:nosplit -func gostringnocopy(str *byte) string { - ss := stringStruct{str: unsafe.Pointer(str), len: findnull(str)} - return *(*string)(unsafe.Pointer(&ss)) -} - -// Here for gccgo unless and until we port string.go. -func atoi(s string) int { - n := 0 - for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { - n = n*10 + int(s[0]) - '0' - s = s[1:] - } - return n -} - // Here for gccgo until we port mgc.go. var writeBarrier struct { enabled bool // compiler emits a check of this before calling write barrier @@ -445,3 +364,6 @@ func releaseSudog(s *sudog) { // Temporary hack for gccgo until we port the garbage collector. func typeBitsBulkBarrier(typ *_type, p, size uintptr) {} + +// Here for gccgo until we port msize.go. +func roundupsize(uintptr) uintptr |