diff options
author | Ondřej Surý <ondrej@sury.org> | 2011-06-10 10:59:18 +0200 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2011-06-10 10:59:18 +0200 |
commit | c29cace1e8f3260389ea78fa4ef86d80cd5e5275 (patch) | |
tree | 947e4435053998a194caacab99bf614d8fd5bc78 | |
parent | 56135c623a865c501ab31cc940c0e22ece2673f4 (diff) | |
download | golang-c29cace1e8f3260389ea78fa4ef86d80cd5e5275.tar.gz |
Imported Upstream version 2011.06.09upstream-weekly/2011.06.09
184 files changed, 8345 insertions, 5628 deletions
@@ -44,6 +44,7 @@ Daniel Theophanes <kardianos@gmail.com> Dave Cheney <dave@cheney.net> David Forsythe <dforsythe@gmail.com> David G. Andersen <dave.andersen@gmail.com> +David Jakob Fritz <david.jakob.fritz@gmail.com> David Titarenco <david.titarenco@gmail.com> Dean Prichard <dean.prichard@gmail.com> Devon H. O'Dell <devon.odell@gmail.com> @@ -54,6 +55,7 @@ Eoghan Sherry <ejsherry@gmail.com> Eric Clark <zerohp@gmail.com> Eric Eisner <eric.d.eisner@gmail.com> Evan Shaw <chickencha@gmail.com> +Fan Hongjian <fan.howard@gmail.com> Fazlul Shahriar <fshahriar@gmail.com> Firmansyah Adiputra <frm.adiputra@gmail.com> Florian Uekermann <florian@uekermann-online.de> @@ -72,9 +74,11 @@ James Toy <nil@opensesame.st> James Whitehead <jnwhiteh@gmail.com> Jan H. Hosang <jan.hosang@gmail.com> Jan Mercl <befelemepeseveze@gmail.com> +Jeff Hodges <jeff@somethingsimilar.com> Jeff R. Allen <jra@nella.org> Jim McGrath <jimmc2@gmail.com> Joe Poirier <jdpoirier@gmail.com> +Jonathan Mark <jhmark@xenops.com> Jonathan Wills <runningwild@gmail.com> Jose Luis Vázquez González <josvazg@gmail.com> Josh Goebel <dreamer3@gmail.com> diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 0fd2b9fe5..a395c11cc 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -81,6 +81,7 @@ David Anderson <danderson@google.com> David Crawshaw <david.crawshaw@zentus.com> David Forsythe <dforsythe@gmail.com> David G. Andersen <dave.andersen@gmail.com> +David Jakob Fritz <david.jakob.fritz@gmail.com> David Symonds <dsymonds@golang.org> David Titarenco <david.titarenco@gmail.com> Dean Prichard <dean.prichard@gmail.com> @@ -93,6 +94,7 @@ Eoghan Sherry <ejsherry@gmail.com> Eric Clark <zerohp@gmail.com> Eric Eisner <eric.d.eisner@gmail.com> Evan Shaw <chickencha@gmail.com> +Fan Hongjian <fan.howard@gmail.com> Fazlul Shahriar <fshahriar@gmail.com> Firmansyah Adiputra <frm.adiputra@gmail.com> Florian Uekermann <florian@uekermann-online.de> <f1@uekermann-online.de> @@ -116,12 +118,14 @@ James Whitehead <jnwhiteh@gmail.com> Jamie Gennis <jgennis@google.com> Jan H. Hosang <jan.hosang@gmail.com> Jan Mercl <befelemepeseveze@gmail.com> +Jeff Hodges <jeff@somethingsimilar.com> Jeff R. Allen <jra@nella.org> <jeff.allen@gmail.com> Jim McGrath <jimmc2@gmail.com> Joe Poirier <jdpoirier@gmail.com> Johan Euphrosine <proppy@google.com> John DeNero <denero@google.com> Jonathan Allie <jonallie@google.com> +Jonathan Mark <jhmark@xenops.com> <jhmark000@gmail.com> Jonathan Wills <runningwild@gmail.com> Jos Visser <josv@google.com> Jose Luis Vázquez González <josvazg@gmail.com> @@ -150,6 +154,7 @@ Maxim Ushakov <ushakov@google.com> Micah Stetson <micah.stetson@gmail.com> Michael Elkins <michael.elkins@gmail.com> Michael Hoisie <hoisie@gmail.com> +Michael T. Jones <mtj@google.com> <michael.jones@gmail.com> Miek Gieben <miek@miek.nl> <remigius.gieben@gmail.com> Mikael Tillenius <mikti42@gmail.com> Mikio Hara <mikioh.mikioh@gmail.com> diff --git a/doc/devel/weekly.html b/doc/devel/weekly.html index 40b4efe44..e45c48a12 100644 --- a/doc/devel/weekly.html +++ b/doc/devel/weekly.html @@ -14,6 +14,64 @@ hg pull hg update weekly.<i>YYYY-MM-DD</i> </pre> +<h2 id="2011-06-09">2011-06-09</h2> + +<pre> +This release includes changes to the strconv, http, and exp/draw packages. +Client code that uses the http or exp/draw packages will need to be changed, +and code that uses strconv or fmt's "%q" formatting directive merits checking. + +The strconv package's Quote function now escapes only those Unicode code points +not classified as printable by unicode.IsPrint. Previously Quote would escape +all non-ASCII characters. This also affects the fmt package's "%q" formatting +directive. The previous quoting behavior is still available via strconv's new +QuoteToASCII function. + +Most instances of the type map[string][]string in the http package have been +replaced with the new Values type. The http.Values type has the Get, Set, Add, +and Del helper methods to make working with query parameters and form values +more convenient. + +The exp/draw package has been split into the image/draw and exp/gui packages. + +Other changes: +* 8l, ld: initial adjustments for Plan 9 native compilation of 8l (thanks Lucio De Re). +* arm: floating point improvements (thanks Fan Hongjian). +* big: Improved speed of nat-to-string conversion (thanks Michael T. Jones), + Rat outputs the requested precision from FloatString (thanks Graham Miller), + gobs for big.Rats. +* cgo: support non intel gcc machine flags (thanks Dave Cheney). +* compress/lzw: do not use background goroutines, + reduce decoder buffer size from 3*4096 to 2*4096. +* crypto/twofish: fix Reset index overflow bug. +* crypto: reorg, cleanup and add function for generating CRLs. +* exec: export the underlying *os.Process in Cmd. +* gc: enable building under clang/2.9 (thanks Dave Cheney), + preparatory work toward escape analysis, compact stack frames. +* go/build: new incomplete package for building go programs. +* godefs: do not assume forward type references are enums (thanks Robert Hencke). +* gofix, gofmt: fix diff regression from exec change. +* html: improve attribute parsing, note package status. +* http: don't fail on accept hitting EMFILE, + fix handling of 0-length HTTP requests. +* image/draw: fix clipping bug where sp/mp were not shifted when r.Min was. +* image/gif: fix buglet in graphics extension. +* image/tiff: support for bit depths other than 8 (thanks Benny Siegert). +* ld: fix and simplify ELF symbol generation (thanks Anthony Martin) +* libmach: use the standardized format for designated initializers (thanks Jeff Hodges) +* mail: address list parsing. +* net: add network interface identification API (thanks Mikio Hara), + fix bug in net.Interfaces: handle elastic sdl_data size correctly (thanks Mikio Hara). +* netchan: added drain method to importer (thanks David Jakob Fritz). +* os: add Process.Kill and Process.Signal (thanks Evan Shaw), + fix Getenv for Plan 9 (thanks Yuval Pavel Zholkover). +* runtime: improve memmove by checking memory overlap (thanks Quan Yong Zhai), + support for Linux grsecurity systems (thanks Jonathan Mark). +* spec: handle a corner case for shifts. +* testing: check that tests and benchmarks do not affect GOMAXPROCS (thanks Dmitriy Vyukov). +* unicode: add IsPrint and related properties, general categories. +</pre> + <h2 id="2011-06-02">2011-06-02</h2> <pre> @@ -22,8 +80,8 @@ to client code. The exec package has been re-designed with a more convenient and succinct API. This code: - args := []string{“diff”, “-u”, “file1.txt”, “file2.txt”} - p, err := exec.Run(“/usr/bin/diff”, args, os.Environ(), "", + args := []string{"diff", "-u", "file1.txt", "file2.txt"} + p, err := exec.Run("/usr/bin/diff", args, os.Environ(), "", exec.DevNull, exec.Pipe, exec.DevNull) if err != nil { return nil, err @@ -37,7 +95,7 @@ This code: } return buf.Bytes(), err can be rewritten as: - return exec.Command(“diff”, “-u”, “file1.txt”, “file2.txt”).Output() + return exec.Command("diff", "-u", "file1.txt", "file2.txt").Output() See the exec package documentation for the details ("godoc exec"). By setting the GOPATH environment variable you can use goinstall to build and @@ -293,7 +351,7 @@ Other changes: <pre> This release includes revisions to the reflect package to make it more -efficient, after the last weekly’s major API update. If your code uses reflect +efficient, after the last weekly's major API update. If your code uses reflect it may require further changes, not all of which can be made automatically by gofix. For the full details of the change, see http://codereview.appspot.com/4435042 @@ -443,7 +501,7 @@ The os package's Open function has been replaced by three functions: Open(name) // same as old Open(name, O_RDONLY, 0) Create(name) // same as old Open(name, O_RDWR|O_TRUNC|O_CREAT, 0666) -To update your code to use the new APIs, run "gofix path/to/code". Gofix can’t +To update your code to use the new APIs, run "gofix path/to/code". Gofix can't handle all situations perfectly, so read and test the changes it makes before committing them. @@ -573,8 +631,8 @@ produce memory and CPU profiling data when invoked with the -test.memprofile and -test.cpuprofile flags. Run "godoc gotest" for details. We have also introduced a way for tests to run quickly when an exhaustive test -is unnecessary. Gotest’s new -test.short flag in combination with the testing -package’s new Short function allows you to write tests that can be run in +is unnecessary. Gotest's new -test.short flag in combination with the testing +package's new Short function allows you to write tests that can be run in normal or "short" mode; short mode is now used by all.bash to reduce installation time. The Makefiles know about the flag - you can just run "make testshort". diff --git a/doc/go_spec.html b/doc/go_spec.html index 0c08e1464..abf5b8f50 100644 --- a/doc/go_spec.html +++ b/doc/go_spec.html @@ -1,5 +1,5 @@ <!-- title The Go Programming Language Specification --> -<!-- subtitle Version of May 24, 2011 --> +<!-- subtitle Version of June 7, 2011 --> <!-- TODO @@ -2720,7 +2720,9 @@ The right operand in a shift expression must have unsigned integer type or be an untyped constant that can be converted to unsigned integer type. If the left operand of a non-constant shift expression is an untyped constant, the type of the constant is what it would be if the shift expression were -replaced by its left operand alone. +replaced by its left operand alone; the type is <code>int</code> if it cannot +be determined from the context (for instance, if the shift expression is an +operand in a comparison against an untyped constant). </p> <pre> @@ -2729,6 +2731,9 @@ var i = 1<<s // 1 has type int var j int32 = 1<<s // 1 has type int32; j == 0 var k = uint64(1<<s) // 1 has type uint64; k == 1<<33 var m int = 1.0<<s // legal: 1.0 has type int +var n = 1.0<<s != 0 // legal: 1.0 has type int; n == false if ints are 32bits in size +var o = 1<<s == 2<<s // legal: 1 and 2 have type int; o == true if ints are 32bits in size +var p = 1<<s == 1<<33 // illegal if ints are 32bits in size: 1 has type int, but 1<<33 overflows int var u = 1.0<<s // illegal: 1.0 has type float64, cannot shift var v float32 = 1<<s // illegal: 1 has type float32, cannot shift var w int64 = 1.0<<33 // legal: 1.0<<33 is a constant shift expression diff --git a/doc/go_tutorial.html b/doc/go_tutorial.html index cfdd0ec6e..d200036b0 100644 --- a/doc/go_tutorial.html +++ b/doc/go_tutorial.html @@ -29,9 +29,9 @@ Let's start in the usual way: <p> <pre> <!-- progs/helloworld.go /package/ END --> 05 package main -<p> + 07 import fmt "fmt" // Package implementing formatted I/O. -<p> + 09 func main() { 10 fmt.Printf("Hello, world; or Καλημέρα κόσμε; or こんにちは 世界\n") 11 } @@ -119,19 +119,19 @@ Next up, here's a version of the Unix utility <code>echo(1)</code>: <p> <pre> <!-- progs/echo.go /package/ END --> 05 package main -<p> + 07 import ( 08 "os" 09 "flag" // command line option parser 10 ) -<p> + 12 var omitNewline = flag.Bool("n", false, "don't print final newline") -<p> + 14 const ( 15 Space = " " 16 Newline = "\n" 17 ) -<p> + 19 func main() { 20 flag.Parse() // Scans the arg list and sets up flags 21 var s string = "" @@ -479,12 +479,12 @@ open/close/read/write interface. Here's the start of <code>file.go</code>: <p> <pre> <!-- progs/file.go /package/ /^}/ --> 05 package file -<p> + 07 import ( 08 "os" 09 "syscall" 10 ) -<p> + 12 type File struct { 13 fd int // file descriptor number 14 name string // file name at Open time @@ -601,7 +601,7 @@ the tricky standard arguments to open and, especially, to create a file: 41 O_CREATE = syscall.O_CREAT 42 O_TRUNC = syscall.O_TRUNC 43 ) -<p> + 45 func Open(name string) (file *File, err os.Error) { 46 return OpenFile(name, O_RDONLY, 0) 47 } @@ -632,7 +632,7 @@ each of which declares a receiver variable <code>file</code>. 61 } 62 return nil 63 } -<p> + 65 func (file *File) Read(b []byte) (ret int, err os.Error) { 66 if file == nil { 67 return -1, os.EINVAL @@ -643,7 +643,7 @@ each of which declares a receiver variable <code>file</code>. 72 } 73 return int(r), err 74 } -<p> + 76 func (file *File) Write(b []byte) (ret int, err os.Error) { 77 if file == nil { 78 return -1, os.EINVAL @@ -654,7 +654,7 @@ each of which declares a receiver variable <code>file</code>. 83 } 84 return int(r), err 85 } -<p> + 87 func (file *File) String() string { 88 return file.name 89 } @@ -677,13 +677,13 @@ We can now use our new package: <p> <pre> <!-- progs/helloworld3.go /package/ END --> 05 package main -<p> + 07 import ( 08 "./file" 09 "fmt" 10 "os" 11 ) -<p> + 13 func main() { 14 hello := []byte("hello, world\n") 15 file.Stdout.Write(hello) @@ -720,14 +720,14 @@ Building on the <code>file</code> package, here's a simple version of the Unix u <p> <pre> <!-- progs/cat.go /package/ END --> 05 package main -<p> + 07 import ( 08 "./file" 09 "flag" 10 "fmt" 11 "os" 12 ) -<p> + 14 func cat(f *file.File) { 15 const NBUF = 512 16 var buf [NBUF]byte @@ -746,7 +746,7 @@ Building on the <code>file</code> package, here's a simple version of the Unix u 29 } 30 } 31 } -<p> + 33 func main() { 34 flag.Parse() // Scans the arg list and sets up flags 35 if flag.NArg() == 0 { @@ -811,11 +811,11 @@ we have a second implementation of the <code>reader</code> interface. 31 type rotate13 struct { 32 source reader 33 } -<p> + 35 func newRotate13(source reader) *rotate13 { 36 return &rotate13{source} 37 } -<p> + 39 func (r13 *rotate13) Read(b []byte) (ret int, err os.Error) { 40 r, e := r13.source.Read(b) 41 for i := 0; i < r; i++ { @@ -823,7 +823,7 @@ we have a second implementation of the <code>reader</code> interface. 43 } 44 return r, e 45 } -<p> + 47 func (r13 *rotate13) String() string { 48 return r13.source.String() 49 } @@ -844,7 +844,7 @@ and use it from within a mostly unchanged <code>cat</code> function: 52 func cat(r reader) { 53 const NBUF = 512 54 var buf [NBUF]byte -<p> + 56 if *rot13Flag { 57 r = newRotate13(r) 58 } @@ -937,7 +937,7 @@ arrays of integers, strings, etc.; here's the code for arrays of <code>int</code <p> <pre> <!-- progs/sort.go /type.*IntArray/ /Swap/ --> 33 type IntArray []int -<p> + 35 func (p IntArray) Len() int { return len(p) } 36 func (p IntArray) Less(i, j int) bool { return p[i] < p[j] } 37 func (p IntArray) Swap(i, j int) { p[i], p[j] = p[j], p[i] } @@ -970,11 +970,11 @@ to implement the three methods for that type, like this: 32 shortName string 33 longName string 34 } -<p> + 36 type dayArray struct { 37 data []*day 38 } -<p> + 40 func (p *dayArray) Len() int { return len(p.data) } 41 func (p *dayArray) Less(i, j int) bool { return p.data[i].num < p.data[j].num } 42 func (p *dayArray) Swap(i, j int) { p.data[i], p.data[j] = p.data[j], p.data[i] } @@ -1065,11 +1065,11 @@ Here's a simple example. 10 a int 11 b string 12 } -<p> + 14 func (t *testType) String() string { 15 return fmt.Sprint(t.a) + " " + t.b 16 } -<p> + 18 func main() { 19 t := &testType{77, "Sunset Strip"} 20 fmt.Println(t) @@ -1347,7 +1347,7 @@ code that invokes the operation and responds to the request: <p> <pre> <!-- progs/server.go /type.binOp/ /^}/ --> 14 type binOp func(a, b int) int -<p> + 16 func run(op binOp, req *request) { 17 reply := op(req.a, req.b) 18 req.replyc <- reply diff --git a/doc/htmlgen.go b/doc/htmlgen.go index 4d68767c3..3a8feb8bc 100644 --- a/doc/htmlgen.go +++ b/doc/htmlgen.go @@ -5,7 +5,7 @@ // Process plain text into HTML. // - h2's are made from lines followed by a line "----\n" // - tab-indented blocks become <pre> blocks -// - blank lines become <p> marks +// - blank lines become <p> marks (except inside <pre> tags) // - "quoted strings" become <code>quoted strings</code> package main @@ -35,9 +35,9 @@ var ( func main() { read() headings() - paragraphs() coalesce(preStart, foldPre) coalesce(tab, foldTabs) + paragraphs() quotes() write() } diff --git a/misc/dashboard/builder/exec.go b/misc/dashboard/builder/exec.go index 0db509136..a042c5699 100644 --- a/misc/dashboard/builder/exec.go +++ b/misc/dashboard/builder/exec.go @@ -27,8 +27,11 @@ func run(envv []string, dir string, argv ...string) os.Error { } // runLog runs a process and returns the combined stdout/stderr, -// as well as writing it to logfile (if specified). -func runLog(envv []string, logfile, dir string, argv ...string) (output string, exitStatus int, err os.Error) { +// as well as writing it to logfile (if specified). It returns +// process combined stdout and stderr output, exit status and error. +// The error returned is nil, if process is started successfully, +// even if exit status is not 0. +func runLog(envv []string, logfile, dir string, argv ...string) (string, int, os.Error) { if *verbose { log.Println("runLog", argv) } @@ -39,7 +42,7 @@ func runLog(envv []string, logfile, dir string, argv ...string) (output string, if logfile != "" { f, err := os.OpenFile(logfile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) if err != nil { - return + return "", 0, err } defer f.Close() w = io.MultiWriter(f, b) @@ -51,15 +54,13 @@ func runLog(envv []string, logfile, dir string, argv ...string) (output string, cmd.Stdout = w cmd.Stderr = w - err = cmd.Run() - output = b.String() + err := cmd.Run() if err != nil { if ws, ok := err.(*os.Waitmsg); ok { - exitStatus = ws.ExitStatus() + return b.String(), ws.ExitStatus(), nil } - return } - return + return b.String(), 0, nil } // useBash prefixes a list of args with 'bash' if the first argument diff --git a/misc/dashboard/builder/http.go b/misc/dashboard/builder/http.go index 4546f855a..5e1da0c87 100644 --- a/misc/dashboard/builder/http.go +++ b/misc/dashboard/builder/http.go @@ -26,18 +26,18 @@ func dash(meth, cmd string, resp interface{}, args param) os.Error { log.Println("dash", cmd, args) } cmd = "http://" + *dashboard + "/" + cmd + vals := make(http.Values) + for k, v := range args { + vals.Add(k, v) + } switch meth { case "GET": - if args != nil { - m := make(map[string][]string) - for k, v := range args { - m[k] = []string{v} - } - cmd += "?" + http.EncodeQuery(m) + if q := vals.Encode(); q != "" { + cmd += "?" + q } r, err = http.Get(cmd) case "POST": - r, err = http.PostForm(cmd, args) + r, err = http.PostForm(cmd, vals) default: return fmt.Errorf("unknown method %q", meth) } diff --git a/misc/dashboard/builder/main.go b/misc/dashboard/builder/main.go index 5ba5c11c3..9377fbe32 100644 --- a/misc/dashboard/builder/main.go +++ b/misc/dashboard/builder/main.go @@ -294,7 +294,7 @@ func (b *Builder) buildHash(hash string) (err os.Error) { logfile := path.Join(workpath, "build.log") buildLog, status, err := runLog(b.envv(), logfile, srcDir, *buildCmd) if err != nil { - return fmt.Errorf("all.bash: %s", err) + return fmt.Errorf("%s: %s", *buildCmd, err) } // if we're in external mode, build all packages and return diff --git a/misc/dashboard/godashboard/app.yaml b/misc/dashboard/godashboard/app.yaml index 4fd05f259..83611cf90 100644 --- a/misc/dashboard/godashboard/app.yaml +++ b/misc/dashboard/godashboard/app.yaml @@ -4,6 +4,10 @@ runtime: python api_version: 1 handlers: +- url: /favicon\.ico + static_files: static/favicon.ico + upload: static/favicon\.ico + - url: /static static_dir: static diff --git a/misc/dashboard/godashboard/gobuild.py b/misc/dashboard/godashboard/gobuild.py index ee700c73b..5678f2e1b 100644 --- a/misc/dashboard/godashboard/gobuild.py +++ b/misc/dashboard/godashboard/gobuild.py @@ -376,8 +376,8 @@ def getBrokenCommit(node, builder): return def firstResult(builder): - q = Commit.all().order('-__key__').limit(20) - for c in q: + q = Commit.all().order('-__key__') + for c in q.fetch(20): for i, b in enumerate(c.builds): p = b.split('`', 1) if p[0] == builder: @@ -392,9 +392,12 @@ def nodeAfter(c): def notifyBroken(c, builder): def send(): - n = Commit.get_by_key_name('%08x-%s' % (c.num, c.node)) - if n.fail_notification_sent: - return False + n = Commit.get(c.key()) + if n is None: + logging.error("couldn't retrieve Commit '%s'" % c.key()) + return False + if n.fail_notification_sent: + return False n.fail_notification_sent = True return n.put() if not db.run_in_transaction(send): diff --git a/misc/dashboard/godashboard/static/favicon.ico b/misc/dashboard/godashboard/static/favicon.ico Binary files differnew file mode 100644 index 000000000..48854ff3b --- /dev/null +++ b/misc/dashboard/godashboard/static/favicon.ico diff --git a/src/cmd/5a/lex.c b/src/cmd/5a/lex.c index a04cda220..3978f1a6c 100644 --- a/src/cmd/5a/lex.c +++ b/src/cmd/5a/lex.c @@ -338,6 +338,8 @@ struct "NRMD", LTYPEI, ANRMD, */ + "SQRTF", LTYPEI, ASQRTF, + "SQRTD", LTYPEI, ASQRTD, "CMPF", LTYPEL, ACMPF, "CMPD", LTYPEL, ACMPD, "ADDF", LTYPEK, AADDF, diff --git a/src/cmd/5g/gg.h b/src/cmd/5g/gg.h index fe404ed79..ce4558e21 100644 --- a/src/cmd/5g/gg.h +++ b/src/cmd/5g/gg.h @@ -23,6 +23,7 @@ struct Addr char sval[NSNAME]; Sym* sym; + Node* node; int width; uchar type; char name; diff --git a/src/cmd/5g/gsubr.c b/src/cmd/5g/gsubr.c index 83a9949d6..bc39912ea 100644 --- a/src/cmd/5g/gsubr.c +++ b/src/cmd/5g/gsubr.c @@ -1101,6 +1101,7 @@ naddr(Node *n, Addr *a, int canemitcode) a->type = D_NONE; a->name = D_NONE; a->reg = NREG; + a->node = N; if(n == N) return; @@ -1189,6 +1190,8 @@ naddr(Node *n, Addr *a, int canemitcode) break; case PAUTO: a->name = D_AUTO; + if (n->sym) + a->node = n->orig; break; case PPARAM: case PPARAMOUT: diff --git a/src/cmd/5g/reg.c b/src/cmd/5g/reg.c index 1cbeb3e3d..5fba02c9e 100644 --- a/src/cmd/5g/reg.c +++ b/src/cmd/5g/reg.c @@ -32,6 +32,8 @@ #include "gg.h" #include "opt.h" +#define NREGVAR 24 +#define REGBITS ((uint32)0xffffff) #define P2R(p) (Reg*)(p->reg) void addsplits(void); @@ -128,6 +130,33 @@ setaddrs(Bits bit) } } +static char* regname[] = { + ".R0", + ".R1", + ".R2", + ".R3", + ".R4", + ".R5", + ".R6", + ".R7", + ".R8", + ".R9", + ".R10", + ".R11", + ".R12", + ".R13", + ".R14", + ".R15", + ".F0", + ".F1", + ".F2", + ".F3", + ".F4", + ".F5", + ".F6", + ".F7", +}; + void regopt(Prog *firstp) { @@ -136,7 +165,7 @@ regopt(Prog *firstp) int i, z, nr; uint32 vreg; Bits bit; - + if(first == 0) { fmtinstall('Q', Qconv); } @@ -164,7 +193,17 @@ regopt(Prog *firstp) r1 = R; firstr = R; lastr = R; - nvar = 0; + + /* + * control flow is more complicated in generated go code + * than in generated c code. define pseudo-variables for + * registers, so we have complete register usage information. + */ + nvar = NREGVAR; + memset(var, 0, NREGVAR*sizeof var[0]); + for(i=0; i<NREGVAR; i++) + var[i].sym = lookup(regname[i]); + regbits = RtoB(REGSP)|RtoB(REGLINK)|RtoB(REGPC); for(z=0; z<BITS; z++) { externs.b[z] = 0; @@ -223,6 +262,16 @@ regopt(Prog *firstp) bit = mkvar(r, &p->from); for(z=0; z<BITS; z++) r->use1.b[z] |= bit.b[z]; + + /* + * middle always read when present + */ + if(p->reg != NREG) { + if(p->from.type != D_FREG) + r->use1.b[0] |= RtoB(p->reg); + else + r->use1.b[0] |= FtoB(p->reg); + } /* * right side depends on opcode @@ -233,6 +282,67 @@ regopt(Prog *firstp) default: yyerror("reg: unknown op: %A", p->as); break; + + /* + * right side read + */ + case ATST: + case ATEQ: + case ACMP: + case ACMN: + case ACMPD: + case ACMPF: + rightread: + for(z=0; z<BITS; z++) + r->use2.b[z] |= bit.b[z]; + break; + + /* + * right side read or read+write, depending on middle + * ADD x, z => z += x + * ADD x, y, z => z = x + y + */ + case AADD: + case AAND: + case AEOR: + case ASUB: + case ARSB: + case AADC: + case ASBC: + case ARSC: + case AORR: + case ABIC: + case ASLL: + case ASRL: + case ASRA: + case AMUL: + case AMULU: + case ADIV: + case AMOD: + case AMODU: + case ADIVU: + if(p->reg != NREG) + goto rightread; + // fall through + + /* + * right side read+write + */ + case AADDF: + case AADDD: + case ASUBF: + case ASUBD: + case AMULF: + case AMULD: + case ADIVF: + case ADIVD: + case AMULAL: + case AMULALU: + for(z=0; z<BITS; z++) { + r->use2.b[z] |= bit.b[z]; + r->set.b[z] |= bit.b[z]; + } + break; /* * right side write @@ -240,11 +350,22 @@ regopt(Prog *firstp) case ANOP: case AMOVB: case AMOVBU: + case AMOVD: + case AMOVDF: + case AMOVDW: + case AMOVF: + case AMOVFW: case AMOVH: case AMOVHU: case AMOVW: - case AMOVF: - case AMOVD: + case AMOVWD: + case AMOVWF: + case AMVN: + case AMULL: + case AMULLU: + if((p->scond & C_SCOND) != C_SCOND_NONE) + for(z=0; z<BITS; z++) + r->use2.b[z] |= bit.b[z]; for(z=0; z<BITS; z++) r->set.b[z] |= bit.b[z]; break; @@ -397,6 +518,24 @@ loop2: } /* + * pass 4.5 + * move register pseudo-variables into regu. + */ + for(r = firstr; r != R; r = r->link) { + r->regu = (r->refbehind.b[0] | r->set.b[0]) & REGBITS; + + r->set.b[0] &= ~REGBITS; + r->use1.b[0] &= ~REGBITS; + r->use2.b[0] &= ~REGBITS; + r->refbehind.b[0] &= ~REGBITS; + r->refahead.b[0] &= ~REGBITS; + r->calbehind.b[0] &= ~REGBITS; + r->calahead.b[0] &= ~REGBITS; + r->regdiff.b[0] &= ~REGBITS; + r->act.b[0] &= ~REGBITS; + } + + /* * pass 5 * isolate regions * calculate costs (paint1) @@ -606,6 +745,7 @@ addmove(Reg *r, int bn, int rn, int f) a = &p1->to; a->sym = v->sym; a->name = v->name; + a->node = v->node; a->offset = v->offset; a->etype = v->etype; a->type = D_OREG; @@ -715,21 +855,40 @@ mkvar(Reg *r, Adr *a) goto onereg; case D_REGREG: + bit = zbits; if(a->offset != NREG) - r->regu |= RtoB(a->offset); - goto onereg; + bit.b[0] |= RtoB(a->offset); + if(a->reg != NREG) + bit.b[0] |= RtoB(a->reg); + return bit; case D_REG: case D_SHIFT: - case D_OREG: onereg: - if(a->reg != NREG) - r->regu |= RtoB(a->reg); + if(a->reg != NREG) { + bit = zbits; + bit.b[0] = RtoB(a->reg); + return bit; + } + break; + + case D_OREG: + if(a->reg != NREG) { + if(a == &r->prog->from) + r->use1.b[0] |= RtoB(a->reg); + else + r->use2.b[0] |= RtoB(a->reg); + if(r->prog->scond & (C_PBIT|C_WBIT)) + r->set.b[0] |= RtoB(a->reg); + } break; case D_FREG: - if(a->reg != NREG) - r->regu |= FtoB(a->reg); + if(a->reg != NREG) { + bit = zbits; + bit.b[0] = FtoB(a->reg); + return bit; + } break; } @@ -795,7 +954,8 @@ mkvar(Reg *r, Adr *a) v->etype = et; v->width = w; v->addr = flag; // funny punning - + v->node = a->node; + if(debug['R']) print("bit=%2d et=%E pun=%d %D\n", i, et, flag, a); diff --git a/src/cmd/5l/5.out.h b/src/cmd/5l/5.out.h index 002b46d45..cf86ae48b 100644 --- a/src/cmd/5l/5.out.h +++ b/src/cmd/5l/5.out.h @@ -53,8 +53,6 @@ #define REGLINK 14 #define REGPC 15 -#define REGTMPT 7 /* used by the loader for thumb code */ - #define NFREG 8 #define FREGRET 0 #define FREGEXT 7 @@ -126,6 +124,8 @@ enum as AMULD, ADIVF, ADIVD, + ASQRTF, + ASQRTD, ASRL, ASRA, diff --git a/src/cmd/5l/Makefile b/src/cmd/5l/Makefile index c11ebe990..9f4a192aa 100644 --- a/src/cmd/5l/Makefile +++ b/src/cmd/5l/Makefile @@ -22,7 +22,6 @@ OFILES=\ optab.$O\ pass.$O\ prof.$O\ - thumb.$O\ softfloat.$O\ span.$O\ symtab.$O\ diff --git a/src/cmd/5l/asm.c b/src/cmd/5l/asm.c index 81c08e353..4afed2b80 100644 --- a/src/cmd/5l/asm.c +++ b/src/cmd/5l/asm.c @@ -34,8 +34,6 @@ #include "../ld/lib.h" #include "../ld/elf.h" -int32 OFFSET; - static Prog *PP; char linuxdynld[] = "/lib/ld-linux.so.2"; @@ -295,7 +293,7 @@ asmb(void) { int32 t; int a, dynsym; - uint32 fo, symo, startva, elfsymo, elfstro, elfsymsize; + uint32 fo, symo, startva; ElfEhdr *eh; ElfPhdr *ph, *pph; ElfShdr *sh; @@ -305,10 +303,6 @@ asmb(void) Bprint(&bso, "%5.2f asmb\n", cputime()); Bflush(&bso); - elfsymsize = 0; - elfstro = 0; - elfsymo = 0; - sect = segtext.sect; seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0); codeblk(sect->vaddr, sect->len); @@ -361,34 +355,28 @@ asmb(void) debug['s'] = 1; break; case Hplan9x32: - OFFSET = HEADR+textsize+segdata.filelen; - seek(cout, OFFSET, 0); + symo = HEADR+segtext.len+segdata.filelen; break; case Hnetbsd: - OFFSET += rnd(segdata.filelen, 4096); - seek(cout, OFFSET, 0); + symo = rnd(segdata.filelen, 4096); break; ElfSym: symo = rnd(HEADR+segtext.filelen, INITRND)+segdata.filelen; symo = rnd(symo, INITRND); break; } + seek(cout, symo, 0); if(iself) { if(debug['v']) Bprint(&bso, "%5.2f elfsym\n", cputime()); - elfsymo = symo+8+symsize+lcsize; - seek(cout, elfsymo, 0); - asmelfsym32(); + asmelfsym(); cflush(); - elfstro = seek(cout, 0, 1); - elfsymsize = elfstro - elfsymo; ewrite(cout, elfstrdat, elfstrsize); // if(debug['v']) // Bprint(&bso, "%5.2f dwarf\n", cputime()); // dwarfemitdebugsections(); } - asmthumbmap(); cflush(); } @@ -397,8 +385,7 @@ asmb(void) if(debug['v']) Bprint(&bso, "%5.2f header\n", cputime()); Bflush(&bso); - OFFSET = 0; - seek(cout, OFFSET, 0); + seek(cout, 0L, 0); switch(HEADTYPE) { case Hnoheader: /* no header */ break; @@ -599,15 +586,15 @@ asmb(void) sh = newElfShdr(elfstr[ElfStrSymtab]); sh->type = SHT_SYMTAB; - sh->off = elfsymo; - sh->size = elfsymsize; + sh->off = symo; + sh->size = symsize; sh->addralign = 4; sh->entsize = 16; sh->link = eh->shnum; // link to strtab sh = newElfShdr(elfstr[ElfStrStrtab]); sh->type = SHT_STRTAB; - sh->off = elfstro; + sh->off = symo+symsize; sh->size = elfstrsize; sh->addralign = 1; @@ -740,59 +727,6 @@ nopstat(char *f, Count *c) (double)(c->outof - c->count)/c->outof); } -static void -outt(int32 f, int32 l) -{ - if(debug['L']) - Bprint(&bso, "tmap: %ux-%ux\n", f, l); - lput(f); - lput(l); -} - -void -asmthumbmap(void) -{ - int32 pc, lastt; - Prog *p; - - if(!seenthumb) - return; - pc = 0; - lastt = -1; - for(cursym = textp; cursym != nil; cursym = cursym->next) { - p = cursym->text; - pc = p->pc - INITTEXT; - setarch(p); - if(thumb){ - if(p->from.sym->foreign){ // 8 bytes of ARM first - if(lastt >= 0){ - outt(lastt, pc-1); - lastt = -1; - } - pc += 8; - } - if(lastt < 0) - lastt = pc; - } - else{ - if(p->from.sym->foreign){ // 4 bytes of THUMB first - if(lastt < 0) - lastt = pc; - pc += 4; - } - if(lastt >= 0){ - outt(lastt, pc-1); - lastt = -1; - } - } - if(cursym->next == nil) - for(; p != P; p = p->link) - pc = p->pc = INITTEXT; - } - if(lastt >= 0) - outt(lastt, pc+1); -} - void asmout(Prog *p, Optab *o, int32 *out) { @@ -816,7 +750,7 @@ if(debug['P']) print("%ux: %P type %d\n", (uint32)(p->pc), p, o->type); break; case 0: /* pseudo ops */ -if(debug['G']) print("%ux: %s: arm %d %d %d\n", (uint32)(p->pc), p->from.sym->name, p->from.sym->thumb, p->from.sym->foreign, p->from.sym->fnptr); +if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p->from.sym->fnptr); break; case 1: /* op R,[R],R */ @@ -880,10 +814,6 @@ if(debug['G']) print("%ux: %s: arm %d %d %d\n", (uint32)(p->pc), p->from.sym->na v = -8; if(p->cond != P) v = (p->cond->pc - pc) - 8; -#ifdef CALLEEBX - if(p->as == ABL) - v += fninc(p->to.sym); -#endif o1 = opbra(p->as, p->scond); o1 |= (v >> 2) & 0xffffff; break; @@ -1198,7 +1128,7 @@ if(debug['G']) print("%ux: %s: arm %d %d %d\n", (uint32)(p->pc), p->from.sym->na r = p->reg; if(r == NREG) { r = rt; - if(p->as == AMOVF || p->as == AMOVD) + if(p->as == AMOVF || p->as == AMOVD || p->as == ASQRTF || p->as == ASQRTD) r = 0; } o1 |= rf | (r<<16) | (rt<<12); @@ -1340,19 +1270,7 @@ if(debug['G']) print("%ux: %s: arm %d %d %d\n", (uint32)(p->pc), p->from.sym->na o2 ^= (1<<6); break; case 74: /* bx $I */ -#ifdef CALLEEBX - diag("bx $i case (arm)"); -#endif - if(!seenthumb) - diag("ABX $I and seenthumb==0"); - v = p->cond->pc; - if(p->to.sym->thumb) - v |= 1; // T bit - o1 = olr(8, REGPC, REGTMP, p->scond&C_SCOND); // mov 8(PC), Rtmp - o2 = oprrr(AADD, p->scond) | immrot(8) | (REGPC<<16) | (REGLINK<<12); // add 8,PC, LR - o3 = ((p->scond&C_SCOND)<<28) | (0x12fff<<8) | (1<<4) | REGTMP; // bx Rtmp - o4 = opbra(AB, 14); // B over o6 - o5 = v; + diag("ABX $I"); break; case 75: /* bx O(R) */ aclass(&p->to); @@ -1371,14 +1289,7 @@ if(debug['G']) print("%ux: %s: arm %d %d %d\n", (uint32)(p->pc), p->from.sym->na o3 = ((p->scond&C_SCOND)<<28) | (0x12fff<<8) | (1<<4) | REGTMP; // BX Rtmp break; case 76: /* bx O(R) when returning from fn*/ - if(!seenthumb) - diag("ABXRET and seenthumb==0"); - aclass(&p->to); -// print("ARM BXRET %d(R%d)\n", instoffset, p->to.reg); - if(instoffset != 0) - diag("non-zero offset in ABXRET"); - // o1 = olr(instoffset, p->to.reg, REGTMP, p->scond); // mov O(R), Rtmp - o1 = ((p->scond&C_SCOND)<<28) | (0x12fff<<8) | (1<<4) | p->to.reg; // BX R + diag("ABXRET"); break; case 77: /* ldrex oreg,reg */ aclass(&p->from); @@ -1632,6 +1543,8 @@ oprrr(int a, int sc) case AMULF: return o | (0xe<<24) | (0x2<<20) | (0xa<<8) | (0<<4); case ADIVD: return o | (0xe<<24) | (0x8<<20) | (0xb<<8) | (0<<4); case ADIVF: return o | (0xe<<24) | (0x8<<20) | (0xa<<8) | (0<<4); + case ASQRTD: return o | (0xe<<24) | (0xb<<20) | (1<<16) | (0xb<<8) | (0xc<<4); + case ASQRTF: return o | (0xe<<24) | (0xb<<20) | (1<<16) | (0xa<<8) | (0xc<<4); case ACMPD: return o | (0xe<<24) | (0xb<<20) | (4<<16) | (0xb<<8) | (0xc<<4); case ACMPF: return o | (0xe<<24) | (0xb<<20) | (4<<16) | (0xa<<8) | (0xc<<4); diff --git a/src/cmd/5l/l.h b/src/cmd/5l/l.h index f3c9d839d..182f3e738 100644 --- a/src/cmd/5l/l.h +++ b/src/cmd/5l/l.h @@ -144,8 +144,6 @@ struct Sym int32 sig; int32 size; uchar special; - uchar thumb; // thumb code - uchar foreign; // called by arm if thumb, by thumb if arm uchar fnptr; // used as fn ptr Sym* hash; // in hash table Sym* allsym; // in all symbol list @@ -208,7 +206,6 @@ enum LFROM = 1<<0, LTO = 1<<1, LPOOL = 1<<2, - V4 = 1<<3, /* arm v4 arch */ C_NONE = 0, C_REG, @@ -221,21 +218,16 @@ enum C_RCON, /* 0xff rotated */ C_NCON, /* ~RCON */ C_SCON, /* 0xffff */ - C_BCON, /* thumb */ C_LCON, C_ZFCON, C_SFCON, C_LFCON, - C_GCON, /* thumb */ C_RACON, - C_SACON, /* thumb */ C_LACON, - C_GACON, /* thumb */ C_SBRA, C_LBRA, - C_GBRA, /* thumb */ C_HAUTO, /* halfword insn offset (-0xff to 0xff) */ C_FAUTO, /* float insn offset (0 to 0x3fc, word aligned) */ @@ -250,12 +242,10 @@ enum C_ROREG, C_SROREG, /* both S and R */ C_LOREG, - C_GOREG, /* thumb */ C_PC, C_SP, C_HREG, - C_OFFPC, /* thumb */ C_ADDR, /* reference to relocatable address */ @@ -287,9 +277,6 @@ EXTERN union #define cbuf u.obuf #define xbuf u.ibuf -#define setarch(p) if((p)->as==ATEXT) thumb=(p)->reg&ALLTHUMBS -#define setthumb(p) if((p)->as==ATEXT) seenthumb|=(p)->reg&ALLTHUMBS - #ifndef COFFCVT EXTERN int32 HEADR; /* length of header */ @@ -319,7 +306,6 @@ EXTERN int nerrors; EXTERN int32 instoffset; EXTERN Opcross opcross[8]; EXTERN Oprang oprange[ALAST]; -EXTERN Oprang thumboprange[ALAST]; EXTERN char* outfile; EXTERN int32 pc; EXTERN uchar repop[ALAST]; @@ -333,14 +319,10 @@ EXTERN int version; EXTERN char xcmp[C_GOK+1][C_GOK+1]; EXTERN Prog zprg; EXTERN int dtype; -EXTERN int armv4; -EXTERN int thumb; -EXTERN int seenthumb; EXTERN int armsize; extern char* anames[]; extern Optab optab[]; -extern Optab thumboptab[]; void addpool(Prog*, Adr*); EXTERN Prog* blitrl; @@ -368,17 +350,13 @@ int Oconv(Fmt*); int Pconv(Fmt*); int Sconv(Fmt*); int aclass(Adr*); -int thumbaclass(Adr*, Prog*); void addhist(int32, int); Prog* appendp(Prog*); void asmb(void); -void asmthumbmap(void); void asmout(Prog*, Optab*, int32*); -void thumbasmout(Prog*, Optab*); int32 atolwhex(char*); Prog* brloop(Prog*); void buildop(void); -void thumbbuildop(void); void buildrep(int, int); void cflush(void); int chipzero(Ieee*); @@ -442,9 +420,6 @@ int32 immaddr(int32); int32 opbra(int, int); int brextra(Prog*); int isbranch(Prog*); -int fnpinc(Sym *); -int fninc(Sym *); -void thumbcount(void); void fnptrs(void); void doelf(void); diff --git a/src/cmd/5l/list.c b/src/cmd/5l/list.c index 2ae25d491..fa838215b 100644 --- a/src/cmd/5l/list.c +++ b/src/cmd/5l/list.c @@ -412,7 +412,6 @@ static char* cnames[] = { [C_ADDR] = "C_ADDR", - [C_BCON] = "C_BCON", [C_FAUTO] = "C_FAUTO", [C_ZFCON] = "C_SFCON", [C_SFCON] = "C_SFCON", @@ -420,11 +419,7 @@ cnames[] = [C_FCR] = "C_FCR", [C_FOREG] = "C_FOREG", [C_FREG] = "C_FREG", - [C_GACON] = "C_GACON", - [C_GBRA] = "C_GBRA", - [C_GCON] = "C_GCON", [C_GOK] = "C_GOK", - [C_GOREG] = "C_GOREG", [C_HAUTO] = "C_HAUTO", [C_HFAUTO] = "C_HFAUTO", [C_HFOREG] = "C_HFOREG", @@ -437,7 +432,6 @@ cnames[] = [C_LOREG] = "C_LOREG", [C_NCON] = "C_NCON", [C_NONE] = "C_NONE", - [C_OFFPC] = "C_OFFPC", [C_PC] = "C_PC", [C_PSR] = "C_PSR", [C_RACON] = "C_RACON", @@ -445,7 +439,6 @@ cnames[] = [C_REG] = "C_REG", [C_REGREG] = "C_REGREG", [C_ROREG] = "C_ROREG", - [C_SACON] = "C_SACON", [C_SAUTO] = "C_SAUTO", [C_SBRA] = "C_SBRA", [C_SCON] = "C_SCON", diff --git a/src/cmd/5l/noop.c b/src/cmd/5l/noop.c index bdcc3cad8..e7c2db5f2 100644 --- a/src/cmd/5l/noop.c +++ b/src/cmd/5l/noop.c @@ -47,74 +47,11 @@ static Sym* sym_modu; static void setdiv(int); -static Prog * -movrr(Prog *q, int rs, int rd, Prog *p) -{ - if(q == nil) - q = prg(); - q->as = AMOVW; - q->line = p->line; - q->from.type = D_REG; - q->from.reg = rs; - q->to.type = D_REG; - q->to.reg = rd; - q->link = p->link; - return q; -} - -static Prog * -fnret(Prog *q, int rs, int foreign, Prog *p) -{ - q = movrr(q, rs, REGPC, p); - if(foreign){ // BX rs - q->as = ABXRET; - q->from.type = D_NONE; - q->from.reg = NREG; - q->to.reg = rs; - } - return q; -} - -static Prog * -aword(int32 w, Prog *p) -{ - Prog *q; - - q = prg(); - q->as = AWORD; - q->line = p->line; - q->from.type = D_NONE; - q->reg = NREG; - q->to.type = D_CONST; - q->to.offset = w; - q->link = p->link; - p->link = q; - return q; -} - -static Prog * -adword(int32 w1, int32 w2, Prog *p) -{ - Prog *q; - - q = prg(); - q->as = ADWORD; - q->line = p->line; - q->from.type = D_CONST; - q->from.offset = w1; - q->reg = NREG; - q->to.type = D_CONST; - q->to.offset = w2; - q->link = p->link; - p->link = q; - return q; -} - void noops(void) { - Prog *p, *q, *q1, *q2; - int o, foreign; + Prog *p, *q, *q1; + int o; Prog *pmorestack; Sym *symmorestack; @@ -140,8 +77,6 @@ noops(void) q = P; for(cursym = textp; cursym != nil; cursym = cursym->next) { for(p = cursym->text; p != P; p = p->link) { - setarch(p); - switch(p->as) { case ATEXT: p->mark |= LEAF; @@ -206,7 +141,6 @@ noops(void) for(cursym = textp; cursym != nil; cursym = cursym->next) { for(p = cursym->text; p != P; p = p->link) { - setarch(p); o = p->as; switch(o) { case ATEXT: @@ -224,54 +158,12 @@ noops(void) Bflush(&bso); cursym->text->mark |= LEAF; } -#ifdef CALLEEBX - if(p->from.sym->foreign){ - if(thumb) - // don't allow literal pool to separate these - p = adword(0xe28f7001, 0xe12fff17, p); // arm add 1, pc, r7 and bx r7 - // p = aword(0xe12fff17, aword(0xe28f7001, p)); // arm add 1, pc, r7 and bx r7 - else - p = aword(0x4778, p); // thumb bx pc and 2 bytes padding - } -#endif if(cursym->text->mark & LEAF) { cursym->leaf = 1; if(!autosize) break; } - if(thumb){ - if(!(p->reg & NOSPLIT)) - diag("stack splitting not supported in thumb"); - if(!(cursym->text->mark & LEAF)){ - q = movrr(nil, REGLINK, REGTMPT-1, p); - p->link = q; - q1 = prg(); - q1->as = AMOVW; - q1->line = p->line; - q1->from.type = D_REG; - q1->from.reg = REGTMPT-1; - q1->to.type = D_OREG; - q1->to.name = D_NONE; - q1->to.reg = REGSP; - q1->to.offset = 0; - q1->link = q->link; - q->link = q1; - } - if(autosize){ - q2 = prg(); - q2->as = ASUB; - q2->line = p->line; - q2->from.type = D_CONST; - q2->from.offset = autosize; - q2->to.type = D_REG; - q2->to.reg = REGSP; - q2->link = p->link; - p->link = q2; - } - break; - } - if(p->reg & NOSPLIT) { q1 = prg(); q1->as = AMOVW; @@ -432,16 +324,9 @@ noops(void) case ARET: nocache(p); - foreign = seenthumb && (cursym->foreign || cursym->fnptr); -// print("%s %d %d\n", cursym->name, cursym->foreign, cursym->fnptr); if(cursym->text->mark & LEAF) { if(!autosize) { - if(thumb){ - p = fnret(p, REGLINK, foreign, p); - break; - } -// if(foreign) print("ABXRET 1 %s\n", cursym->name); - p->as = foreign ? ABXRET : AB; + p->as = AB; p->from = zprg.from; p->to.type = D_OREG; p->to.offset = 0; @@ -449,95 +334,16 @@ noops(void) break; } } - if(thumb){ - diag("thumb not maintained"); - errorexit(); - if(cursym->text->mark & LEAF){ - if(autosize){ - p->as = AADD; - p->from.type = D_CONST; - p->from.offset = autosize; - p->to.type = D_REG; - p->to.reg = REGSP; - q = nil; - } - else - q = p; - q = fnret(q, REGLINK, foreign, p); - if(q != p) - p->link = q; - } - else{ - p->as = AMOVW; - p->from.type = D_OREG; - p->from.name = D_NONE; - p->from.reg = REGSP; - p->from.offset = 0; - p->to.type = D_REG; - p->to.reg = REGTMPT-1; - if(autosize){ - q = prg(); - q->as = AADD; - q->from.type = D_CONST; - q->from.offset = autosize; - q->to.type = D_REG; - q->to.reg = REGSP; - q->link = p->link; - p->link = q; - } - else - q = p; - q1 = fnret(nil, REGTMPT-1, foreign, p); - q1->link = q->link; - q->link = q1; - } - break; - } - if(foreign) { - diag("foreign not maintained"); - errorexit(); -// if(foreign) print("ABXRET 3 %s\n", cursym->name); -#define R 1 - p->as = AMOVW; - p->from.type = D_OREG; - p->from.name = D_NONE; - p->from.reg = REGSP; - p->from.offset = 0; - p->to.type = D_REG; - p->to.reg = R; - q = prg(); - q->as = AADD; - q->scond = p->scond; - q->line = p->line; - q->from.type = D_CONST; - q->from.offset = autosize; - q->to.type = D_REG; - q->to.reg = REGSP; - q->link = p->link; - p->link = q; - q1 = prg(); - q1->as = ABXRET; - q1->scond = p->scond; - q1->line = p->line; - q1->to.type = D_OREG; - q1->to.offset = 0; - q1->to.reg = R; - q1->link = q->link; - q->link = q1; -#undef R - } - else { - p->as = AMOVW; - p->scond |= C_PBIT; - p->from.type = D_OREG; - p->from.offset = autosize; - p->from.reg = REGSP; - p->to.type = D_REG; - p->to.reg = REGPC; - // If there are instructions following - // this ARET, they come from a branch - // with the same stackframe, so no spadj. - } + p->as = AMOVW; + p->scond |= C_PBIT; + p->from.type = D_OREG; + p->from.offset = autosize; + p->from.reg = REGSP; + p->to.type = D_REG; + p->to.reg = REGPC; + // If there are instructions following + // this ARET, they come from a branch + // with the same stackframe, so no spadj. break; case AADD: @@ -589,7 +395,7 @@ noops(void) if(q1->reg == NREG) p->from.reg = q1->to.reg; p->to.type = D_REG; - p->to.reg = prog_div->from.sym->thumb ? REGTMPT : REGTMP; + p->to.reg = REGTMP; p->to.offset = 0; /* CALL appropriate */ @@ -598,14 +404,7 @@ noops(void) p->link = q; p = q; -#ifdef CALLEEBX p->as = ABL; -#else - if(prog_div->from.sym->thumb) - p->as = thumb ? ABL : ABX; - else - p->as = thumb ? ABX : ABL; -#endif p->line = q1->line; p->to.type = D_BRANCH; p->cond = p; @@ -637,7 +436,7 @@ noops(void) p->as = AMOVW; p->line = q1->line; p->from.type = D_REG; - p->from.reg = prog_div->from.sym->thumb ? REGTMPT : REGTMP; + p->from.reg = REGTMP; p->from.offset = 0; p->to.type = D_REG; p->to.reg = q1->to.reg; @@ -669,12 +468,6 @@ noops(void) break; case AMOVW: - if(thumb){ - Adr *a = &p->from; - - if(a->type == D_CONST && ((a->name == D_NONE && a->reg == REGSP) || a->name == D_AUTO || a->name == D_PARAM) && (a->offset & 3)) - diag("SP offset not multiple of 4"); - } if((p->scond & C_WBIT) && p->to.type == D_OREG && p->to.reg == REGSP) p->spadj = -p->to.offset; if((p->scond & C_PBIT) && p->from.type == D_OREG && p->from.reg == REGSP && p->to.reg != REGPC) @@ -682,136 +475,6 @@ noops(void) if(p->from.type == D_CONST && p->from.reg == REGSP && p->to.type == D_REG && p->to.reg == REGSP) p->spadj = -p->from.offset; break; - case AMOVB: - case AMOVBU: - case AMOVH: - case AMOVHU: - if(thumb){ - if(p->from.type == D_OREG && (p->from.name == D_AUTO || p->from.name == D_PARAM || (p->from.name == D_CONST && p->from.reg == REGSP))){ - q = prg(); - *q = *p; - if(p->from.name == D_AUTO) - q->from.offset += autosize; - else if(p->from.name == D_PARAM) - q->from.offset += autosize+4; - q->from.name = D_NONE; - q->from.reg = REGTMPT; - p = movrr(p, REGSP, REGTMPT, p); - q->link = p->link; - p->link = q; - } - if(p->to.type == D_OREG && (p->to.name == D_AUTO || p->to.name == D_PARAM || (p->to.name == D_CONST && p->to.reg == REGSP))){ - q = prg(); - *q = *p; - if(p->to.name == D_AUTO) - q->to.offset += autosize; - else if(p->to.name == D_PARAM) - q->to.offset += autosize+4; - q->to.name = D_NONE; - q->to.reg = REGTMPT; - p = movrr(p, REGSP, REGTMPT, p); - q->link = p->link; - p->link = q; - if(q->to.offset < 0 || q->to.offset > 255){ // complicated - p->to.reg = REGTMPT+1; // mov sp, r8 - q1 = prg(); - q1->line = p->line; - q1->as = AMOVW; - q1->from.type = D_CONST; - q1->from.offset = q->to.offset; - q1->to.type = D_REG; - q1->to.reg = REGTMPT; // mov $o, r7 - p->link = q1; - q1->link = q; - q1 = prg(); - q1->line = p->line; - q1->as = AADD; - q1->from.type = D_REG; - q1->from.reg = REGTMPT+1; - q1->to.type = D_REG; - q1->to.reg = REGTMPT; // add r8, r7 - p->link->link = q1; - q1->link = q; - q->to.offset = 0; // mov* r, 0(r7) - /* phew */ - } - } - } - break; - case AMOVM: - if(thumb){ - if(p->from.type == D_OREG){ - if(p->from.offset == 0) - p->from.type = D_REG; - else - diag("non-zero AMOVM offset"); - } - else if(p->to.type == D_OREG){ - if(p->to.offset == 0) - p->to.type = D_REG; - else - diag("non-zero AMOVM offset"); - } - } - break; - case AB: - if(thumb && p->to.type == D_OREG){ - if(p->to.offset == 0){ - p->as = AMOVW; - p->from.type = D_REG; - p->from.reg = p->to.reg; - p->to.type = D_REG; - p->to.reg = REGPC; - } - else{ - p->as = AADD; - p->from.type = D_CONST; - p->from.offset = p->to.offset; - p->reg = p->to.reg; - p->to.type = D_REG; - p->to.reg = REGTMPT-1; - q = prg(); - q->as = AMOVW; - q->line = p->line; - q->from.type = D_REG; - q->from.reg = REGTMPT-1; - q->to.type = D_REG; - q->to.reg = REGPC; - q->link = p->link; - p->link = q; - } - } - if(seenthumb && !thumb && p->to.type == D_OREG && p->to.reg == REGLINK){ - // print("warn %s: b (R%d) assuming a return\n", cursym->name, p->to.reg); - p->as = ABXRET; - } - break; - case ABL: - case ABX: - if(thumb && p->to.type == D_OREG){ - if(p->to.offset == 0){ - p->as = o; - p->from.type = D_NONE; - p->to.type = D_REG; - } - else{ - p->as = AADD; - p->from.type = D_CONST; - p->from.offset = p->to.offset; - p->reg = p->to.reg; - p->to.type = D_REG; - p->to.reg = REGTMPT-1; - q = prg(); - q->as = o; - q->line = p->line; - q->from.type = D_NONE; - q->to.type = D_REG; - q->to.reg = REGTMPT-1; - q->link = p->link; - p->link = q; - } - } - break; } } } @@ -876,13 +539,19 @@ setdiv(int as) Prog *p = nil; switch(as){ - case ADIV: p = prog_div; break; - case ADIVU: p = prog_divu; break; - case AMOD: p = prog_mod; break; - case AMODU: p = prog_modu; break; + case ADIV: + p = prog_div; + break; + case ADIVU: + p = prog_divu; + break; + case AMOD: + p = prog_mod; + break; + case AMODU: + p = prog_modu; + break; } - if(thumb != p->from.sym->thumb) - p->from.sym->foreign = 1; } void diff --git a/src/cmd/5l/obj.c b/src/cmd/5l/obj.c index 96ba0010f..dd3a7329a 100644 --- a/src/cmd/5l/obj.c +++ b/src/cmd/5l/obj.c @@ -67,6 +67,7 @@ static char* linkername[] = { "runtime.softfloat", + "math.sqrtGoC", }; void @@ -241,7 +242,6 @@ main(int argc, char *argv[]) zprg.from.reg = NREG; zprg.to = zprg.from; buildop(); - thumbbuildop(); // could build on demand histgen = 0; pc = 0; dtype = 4; @@ -285,10 +285,8 @@ main(int argc, char *argv[]) asmb(); undef(); - if(debug['c']){ - thumbcount(); + if(debug['c']) print("ARM size = %d\n", armsize); - } if(debug['v']) { Bprint(&bso, "%5.2f cpu time\n", cputime()); Bprint(&bso, "%d sizeof adr\n", sizeof(Adr)); @@ -405,8 +403,6 @@ nopout(Prog *p) p->to.type = D_NONE; } -static void puntfp(Prog *); - void ldobj1(Biobuf *f, char *pkg, int64 len, char *pn) { @@ -621,8 +617,6 @@ loop: else textp = s; etextp = s; - setarch(p); - setthumb(p); p->align = 4; autosize = (p->to.offset+3L) & ~3L; p->to.offset = autosize; @@ -630,7 +624,6 @@ loop: s->type = STEXT; s->text = p; s->value = pc; - s->thumb = thumb; lastp = p; p->pc = pc; pc++; @@ -672,13 +665,9 @@ loop: case AMULD: case ADIVF: case ADIVD: - if(thumb) - puntfp(p); goto casedef; case AMOVF: - if(thumb) - puntfp(p); if(skip) goto casedef; @@ -700,8 +689,6 @@ loop: goto casedef; case AMOVD: - if(thumb) - puntfp(p); if(skip) goto casedef; @@ -757,17 +744,6 @@ prg(void) return p; } -static void -puntfp(Prog *p) -{ - USED(p); - /* floating point - punt for now */ - cursym->text->reg = NREG; /* ARM */ - cursym->thumb = 0; - thumb = 0; - // print("%s: generating ARM code (contains floating point ops %d)\n", curtext->from.sym->name, p->line); -} - Prog* appendp(Prog *q) { diff --git a/src/cmd/5l/optab.c b/src/cmd/5l/optab.c index 4816aa40f..514786f85 100644 --- a/src/cmd/5l/optab.c +++ b/src/cmd/5l/optab.c @@ -77,7 +77,6 @@ Optab optab[] = { ASWI, C_NONE, C_NONE, C_LCON, 10, 4, 0 }, { AWORD, C_NONE, C_NONE, C_LCON, 11, 4, 0 }, - { AWORD, C_NONE, C_NONE, C_GCON, 11, 4, 0 }, { AWORD, C_NONE, C_NONE, C_ADDR, 11, 4, 0 }, { AMOVW, C_NCON, C_NONE, C_REG, 12, 4, 0 }, @@ -181,34 +180,34 @@ Optab optab[] = { ACASE, C_REG, C_NONE, C_NONE, 62, 4, 0 }, { ABCASE, C_NONE, C_NONE, C_SBRA, 63, 4, 0 }, - { AMOVH, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, V4 }, - { AMOVH, C_REG, C_NONE, C_HOREG, 70, 4, 0, V4 }, - { AMOVHU, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, V4 }, - { AMOVHU, C_REG, C_NONE, C_HOREG, 70, 4, 0, V4 }, - - { AMOVB, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, V4 }, - { AMOVB, C_HOREG,C_NONE, C_REG, 71, 4, 0, V4 }, - { AMOVH, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, V4 }, - { AMOVH, C_HOREG,C_NONE, C_REG, 71, 4, 0, V4 }, - { AMOVHU, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, V4 }, - { AMOVHU, C_HOREG,C_NONE, C_REG, 71, 4, 0, V4 }, - - { AMOVH, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO|V4 }, - { AMOVH, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO|V4 }, - { AMOVH, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO|V4 }, - { AMOVHU, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO|V4 }, - { AMOVHU, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO|V4 }, - { AMOVHU, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO|V4 }, - - { AMOVB, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM|V4 }, - { AMOVB, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM|V4 }, - { AMOVB, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM|V4 }, - { AMOVH, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM|V4 }, - { AMOVH, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM|V4 }, - { AMOVH, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM|V4 }, - { AMOVHU, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM|V4 }, - { AMOVHU, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM|V4 }, - { AMOVHU, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM|V4 }, + { AMOVH, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, 0 }, + { AMOVH, C_REG, C_NONE, C_HOREG, 70, 4, 0, 0 }, + { AMOVHU, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, 0 }, + { AMOVHU, C_REG, C_NONE, C_HOREG, 70, 4, 0, 0 }, + + { AMOVB, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, 0 }, + { AMOVB, C_HOREG,C_NONE, C_REG, 71, 4, 0, 0 }, + { AMOVH, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, 0 }, + { AMOVH, C_HOREG,C_NONE, C_REG, 71, 4, 0, 0 }, + { AMOVHU, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, 0 }, + { AMOVHU, C_HOREG,C_NONE, C_REG, 71, 4, 0, 0 }, + + { AMOVH, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO }, + { AMOVH, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO }, + { AMOVH, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO }, + { AMOVHU, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO }, + { AMOVHU, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO }, + { AMOVHU, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO }, + + { AMOVB, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM }, + { AMOVB, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM }, + { AMOVB, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM }, + { AMOVH, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM }, + { AMOVH, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM }, + { AMOVH, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM }, + { AMOVHU, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM }, + { AMOVHU, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM }, + { AMOVHU, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM }, { ALDREX, C_SOREG,C_NONE, C_REG, 77, 4, 0 }, { ASTREX, C_SOREG,C_REG, C_REG, 78, 4, 0 }, diff --git a/src/cmd/5l/pass.c b/src/cmd/5l/pass.c index 7e1ba6a09..194a1ed5f 100644 --- a/src/cmd/5l/pass.c +++ b/src/cmd/5l/pass.c @@ -100,7 +100,6 @@ xfol(Prog *p, Prog **last) loop: if(p == P) return; - setarch(p); a = p->as; if(a == AB) { q = p->cond; @@ -210,14 +209,7 @@ patch(void) vexit = s->value; for(cursym = textp; cursym != nil; cursym = cursym->next) { for(p = cursym->text; p != P; p = p->link) { - setarch(p); a = p->as; - if(seenthumb && a == ABL){ - // if((s = p->to.sym) != S && (s1 = curtext->from.sym) != S) - // print("%s calls %s\n", s1->name, s->name); - if((s = p->to.sym) != S && s->thumb != cursym->thumb) - s->foreign = 1; - } if((a == ABL || a == ABX || a == AB || a == ARET) && p->to.type != D_BRANCH && p->to.sym != S) { s = p->to.sym; @@ -254,19 +246,7 @@ patch(void) for(cursym = textp; cursym != nil; cursym = cursym->next) { for(p = cursym->text; p != P; p = p->link) { - setarch(p); a = p->as; - if(seenthumb && a == ABL) { -#ifdef CALLEEBX - if(0) - {} -#else - if((s = p->to.sym) != S && (s->foreign || s->fnptr)) - p->as = ABX; -#endif - else if(p->to.type == D_OREG) - p->as = ABX; - } if(p->cond != P) { p->cond = brloop(p->cond); if(p->cond != P) diff --git a/src/cmd/5l/prof.c b/src/cmd/5l/prof.c index ad115a8ca..48ad2dc59 100644 --- a/src/cmd/5l/prof.c +++ b/src/cmd/5l/prof.c @@ -47,7 +47,6 @@ doprof1(void) s = lookup("__mcount", 0); n = 1; for(p = firstp->link; p != P; p = p->link) { - setarch(p); if(p->as == ATEXT) { q = prg(); q->line = p->line; @@ -74,7 +73,7 @@ doprof1(void) p->from.sym = s; p->from.offset = n*4 + 4; p->to.type = D_REG; - p->to.reg = thumb ? REGTMPT : REGTMP; + p->to.reg = REGTMP; q = prg(); q->line = p->line; @@ -86,7 +85,7 @@ doprof1(void) p->from.type = D_CONST; p->from.offset = 1; p->to.type = D_REG; - p->to.reg = thumb ? REGTMPT : REGTMP; + p->to.reg = REGTMP; q = prg(); q->line = p->line; @@ -96,7 +95,7 @@ doprof1(void) p = q; p->as = AMOVW; p->from.type = D_REG; - p->from.reg = thumb ? REGTMPT : REGTMP; + p->from.reg = REGTMP; p->to.type = D_OREG; p->to.name = D_EXTERN; p->to.sym = s; @@ -143,7 +142,6 @@ doprof2(void) ps4 = P; for(cursym = textp; cursym != nil; cursym = cursym->next) { p = cursym->text; - setarch(p); if(cursym == s2) { ps2 = p; p->reg = 1; @@ -155,7 +153,6 @@ doprof2(void) } for(cursym = textp; cursym != nil; cursym = cursym->next) for(p = cursym->text; p != P; p = p->link) { - setarch(p); if(p->as == ATEXT) { if(p->reg & NOPROF) { for(;;) { diff --git a/src/cmd/5l/softfloat.c b/src/cmd/5l/softfloat.c index 03d8c6d26..4f799d17e 100644 --- a/src/cmd/5l/softfloat.c +++ b/src/cmd/5l/softfloat.c @@ -54,6 +54,8 @@ softfloat(void) case AMULD: case ADIVF: case ADIVD: + case ASQRTF: + case ASQRTD: goto soft; default: diff --git a/src/cmd/5l/span.c b/src/cmd/5l/span.c index 4067f1a32..eb79f6b5a 100644 --- a/src/cmd/5l/span.c +++ b/src/cmd/5l/span.c @@ -50,86 +50,6 @@ isbranch(Prog *p) } static int -ispad(Prog *p) -{ - if(p->as != AMOVW) - return 0; - if(p->from.type != D_REG || p->from.reg != REGTMP) - return 0; - if(p->to.type != D_REG || p->to.reg != REGTMP) - return 0; - return 1; -} - -int -fninc(Sym *s) -{ - if(thumb){ - if(s->thumb){ - if(s->foreign) - return 8; - else - return 0; - } - else{ - if(s->foreign) - return 0; - else - diag("T A !foreign in fninc"); - } - } - else{ - if(s->thumb){ - if(s->foreign) - return 0; - else - diag("A T !foreign in fninc"); - } - else{ - if(s->foreign) - return 4; - else - return 0; - } - } - return 0; -} - -int -fnpinc(Sym *s) -{ - if(!s->fnptr){ // a simplified case BX O(R) -> BL O(R) - if(!debug['f']) - diag("fnptr == 0 in fnpinc"); - if(s->foreign) - diag("bad usage in fnpinc %s %d %d", s->name, s->foreign, s->thumb); - return 0; - } - /* 0, 1, 2, 3 squared */ - if(s->thumb) - return s->foreign ? 9 : 1; - else - return s->foreign ? 4 : 0; -} - -static Prog * -pad(Prog *p, int pc) -{ - Prog *q; - - q = prg(); - q->as = AMOVW; - q->line = p->line; - q->from.type = D_REG; - q->from.reg = REGTMP; - q->to.type = D_REG; - q->to.reg = REGTMP; - q->pc = pc; - q->link = p->link; - return q; -} - -static int scan(Prog *op, Prog *p, int c) { Prog *q; @@ -182,7 +102,6 @@ span(void) otxt = c; for(cursym = textp; cursym != nil; cursym = cursym->next) { p = cursym->text; - setarch(p); p->pc = c; cursym->value = c; @@ -193,19 +112,14 @@ span(void) if(c-otxt >= 1L<<17) bflag = 1; otxt = c; - if(thumb && blitrl) - pool.extra += brextra(p); for(op = p, p = p->link; p != P; op = p, p = p->link) { curp = p; - setarch(p); p->pc = c; o = oplook(p); m = o->size; // must check literal pool here in case p generates many instructions if(blitrl){ - if(thumb && isbranch(p)) - pool.extra += brextra(p); if(checkpool(op, p->as == ACASE ? casesz(p) : m)) c = p->pc = scan(op, p, c); } @@ -230,8 +144,6 @@ span(void) c += m; } if(blitrl){ - if(thumb && isbranch(op)) - pool.extra += brextra(op); if(checkpool(op, 0)) c = scan(op, P, c); } @@ -253,10 +165,7 @@ span(void) cursym->value = c; for(p = cursym->text; p != P; p = p->link) { curp = p; - setarch(p); p->pc = c; - if(thumb && isbranch(p)) - nocache(p); o = oplook(p); /* very large branches if(o->type == 6 && p->cond) { @@ -298,74 +207,6 @@ span(void) } } - if(seenthumb){ // branch resolution - int passes = 0; - int lastc = 0; - int again; - Prog *oop; - - loop: - passes++; - if(passes > 100){ - diag("span looping !"); - errorexit(); - } - c = INITTEXT; - oop = op = nil; - again = 0; - for(cursym = textp; cursym != nil; cursym = cursym->next) { - cursym->value = c; - for(p = cursym->text; p != P; oop = op, op = p, p = p->link) { - curp = p; - setarch(p); - if(p->pc != c) - again = 1; - p->pc = c; - if(thumb && isbranch(p)) - nocache(p); - o = oplook(p); - m = o->size; - if(passes == 1 && thumb && isbranch(p)){ // start conservative so unneeded alignment is not added - if(p->as == ABL) - m = 4; - else - m = 2; - p->align = 0; - } - if(p->align){ - if((p->align == 4 && (c&3)) || (p->align == 2 && !(c&3))){ - if(ispad(op)){ - oop->link = p; - op = oop; - c -= 2; - p->pc = c; - } - else{ - op->link = pad(op, c); - op = op->link; - c += 2; - p->pc = c; - } - again = 1; - } - } - if(m == 0) { - if(p->as == ATEXT) { - autosize = p->to.offset + 4; - if(p->from.sym != S) - p->from.sym->value = c; - continue; - } - } - c += m; - } - cursym->size = c - cursym->value; - } - if(c != lastc || again){ - lastc = c; - goto loop; - } - } c = rnd(c, 8); /* @@ -378,7 +219,6 @@ span(void) */ for(cursym = textp; cursym != nil; cursym = cursym->next) { p = cursym->text; - setarch(p); autosize = p->to.offset + 4; symgrow(cursym, cursym->size); @@ -412,13 +252,6 @@ span(void) int checkpool(Prog *p, int sz) { - if(thumb){ - if(pool.size >= 0x3fc || (p->pc+sz+pool.extra+2+2)+(pool.size-4)-pool.start-4 >= 0x3fc) - return flushpool(p, 1, 0); - else if(p->link == P) - return flushpool(p, 2, 0); - return 0; - } if(pool.size >= 0xffc || immaddr((p->pc+sz+4)+4+pool.size - pool.start+8) == 0) return flushpool(p, 1, 0); else if(p->link == P) @@ -441,7 +274,7 @@ flushpool(Prog *p, int skip, int force) q->link = blitrl; blitrl = q; } - else if(!force && (p->pc+pool.size-pool.start < (thumb ? 0x3fc+4-pool.extra : 2048))) + else if(!force && (p->pc+pool.size-pool.start < 2048)) return 0; elitrl->link = p->link; p->link = blitrl; @@ -461,10 +294,7 @@ addpool(Prog *p, Adr *a) Prog *q, t; int c; - if(thumb) - c = thumbaclass(a, p); - else - c = aclass(a); + c = aclass(a); t = zprg; t.as = AWORD; @@ -480,12 +310,10 @@ addpool(Prog *p, Adr *a) case C_FOREG: case C_SOREG: case C_HOREG: - case C_GOREG: case C_FAUTO: case C_SAUTO: case C_LAUTO: case C_LACON: - case C_GACON: t.to.type = D_CONST; t.to.offset = instoffset; break; @@ -591,16 +419,6 @@ symaddr(Sym *s) return 0; case STEXT: -/* TODO(rsc): what is this for? -#ifdef CALLEEBX - v += fnpinc(s); -#else - if(s->thumb) - v++; // T bit -#endif -*/ - break; - case SELFDATA: case SRODATA: case SDATA: @@ -768,35 +586,19 @@ oplook(Prog *p) int a1, a2, a3, r; char *c1, *c3; Optab *o, *e; - Optab *otab; - Oprang *orange; - if(thumb){ - otab = thumboptab; - orange = thumboprange; - } - else{ - otab = optab; - orange = oprange; - } a1 = p->optab; if(a1) - return otab+(a1-1); + return optab+(a1-1); a1 = p->from.class; if(a1 == 0) { - if(thumb) - a1 = thumbaclass(&p->from, p) + 1; - else - a1 = aclass(&p->from) + 1; + a1 = aclass(&p->from) + 1; p->from.class = a1; } a1--; a3 = p->to.class; if(a3 == 0) { - if(thumb) - a3 = thumbaclass(&p->to, p) + 1; - else - a3 = aclass(&p->to) + 1; + a3 = aclass(&p->to) + 1; p->to.class = a3; } a3--; @@ -804,35 +606,35 @@ oplook(Prog *p) if(p->reg != NREG) a2 = C_REG; r = p->as; - o = orange[r].start; + o = oprange[r].start; if(o == 0) { a1 = opcross[repop[r]][a1][a2][a3]; if(a1) { p->optab = a1+1; - return otab+a1; + return optab+a1; } - o = orange[r].stop; /* just generate an error */ + o = oprange[r].stop; /* just generate an error */ } if(debug['O']) { print("oplook %A %O %O %O\n", (int)p->as, a1, a2, a3); print(" %d %d\n", p->from.type, p->to.type); } - e = orange[r].stop; + e = oprange[r].stop; c1 = xcmp[a1]; c3 = xcmp[a3]; for(; o<e; o++) if(o->a2 == a2) if(c1[o->a1]) if(c3[o->a3]) { - p->optab = (o-otab)+1; + p->optab = (o-optab)+1; return o; } diag("illegal combination %A %O %O %O, %d %d", p->as, a1, a2, a3, p->from.type, p->to.type); prasm(p); if(o == 0) - o = otab; + o = optab; return o; } @@ -883,9 +685,6 @@ cmp(int a, int b) if(b == C_SBRA) return 1; break; - case C_GBRA: - if(b == C_SBRA || b == C_LBRA) - return 1; case C_HREG: return cmp(C_SP, b) || cmp(C_PC, b); @@ -905,9 +704,6 @@ ocmp(const void *a1, const void *a2) n = p1->as - p2->as; if(n) return n; - n = (p2->flag&V4) - (p1->flag&V4); /* architecture version */ - if(n) - return n; n = p1->a1 - p2->a1; if(n) return n; @@ -925,15 +721,11 @@ buildop(void) { int i, n, r; - armv4 = 1; for(i=0; i<C_GOK; i++) for(n=0; n<C_GOK; n++) xcmp[i][n] = cmp(n, i); for(n=0; optab[n].as != AXXX; n++) - if((optab[n].flag & V4) && !armv4) { - optab[n].as = AXXX; - break; - } + ; qsort(optab, n, sizeof(optab[0]), ocmp); for(i=0; i<n; i++) { r = optab[i].as; @@ -1023,6 +815,8 @@ buildop(void) oprange[AMULD] = oprange[r]; oprange[ADIVF] = oprange[r]; oprange[ADIVD] = oprange[r]; + oprange[ASQRTF] = oprange[r]; + oprange[ASQRTD] = oprange[r]; oprange[AMOVFD] = oprange[r]; oprange[AMOVDF] = oprange[r]; break; diff --git a/src/cmd/5l/thumb.c b/src/cmd/5l/thumb.c deleted file mode 100644 index a6f729bed..000000000 --- a/src/cmd/5l/thumb.c +++ /dev/null @@ -1,1658 +0,0 @@ -// Inferno utils/5l/thumb.c -// http://code.google.com/p/inferno-os/source/browse/utils/5l/thumb.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#include "l.h" -#include "../ld/lib.h" - -static int32 thumboprr(int); -static int32 thumboprrr(int, int); -static int32 thumbopirr(int , int); -static int32 thumbopri(int); -static int32 thumbophh(int); -static int32 thumbopbra(int); -static int32 thumbopmv(int, int); -static void lowreg(Prog *, int); -static void mult(Prog *, int, int); -static void numr(Prog *, int, int, int); -static void regis(Prog *, int, int, int); -static void dis(int, int); - -// build a constant using neg, add and shift - only worth it if < 6 bytes */ -static int -immbuildcon(int c, Prog *p) -{ - int n = 0; - - USED(p); - if(c >= 0 && c <= 255) - return 0; // mv - if(c >= -255 && c < 0) // mv, neg - return 1; - if(c >= 256 && c <= 510) // mv, add - return 1; - if(c < 0) - return 0; - while(!(c & 1)){ - n++; - c >>= 1; - } - if(c >= 0 && c <= 255) // mv, lsl - return 1; - return 0; -} - -// positive 5 bit offset from register - O(R) -// positive 8 bit offset from register - mov O, R then [R, R] -// otherwise O goes in literal pool - mov O1(PC), R then [R, R] -static int -immoreg(int off, Prog *p) -{ - int v = 1; - int as = p->as; - - if(off < 0) - return C_GOREG; - if(as == AMOVW) - v = 4; - else if(as == AMOVH || as == AMOVHU) - v = 2; - else if(as == AMOVB || as == AMOVBU) - v = 1; - else - diag("bad op in immoreg"); - if(off/v <= 31) - return C_SOREG; - if(off <= 255) - return C_LOREG; - return C_GOREG; -} - -// positive 8 bit - mov O, R then 0(R) -// otherwise O goes in literal pool - mov O1(PC), R then 0(R) -static int -immacon(int off, Prog *p, int t1, int t2) -{ - USED(p); - if(off < 0) - return t2; - if(off <= 255) - return t1; - return t2; -} - -// unsigned 8 bit in words -static int -immauto(int off, Prog *p) -{ - if(p->as != AMOVW) - diag("bad op in immauto"); - mult(p, off, 4); - if(off >= 0 && off <= 1020) - return C_SAUTO; - return C_LAUTO; -} - -static int -immsmall(int off, Prog *p, int t1, int t2, int t3) -{ - USED(p); - if(off >= 0 && off <= 7) - return t1; - if(off >= 0 && off <= 255) - return t2; - return t3; -} - -static int -immcon(int off, Prog *p) -{ - int as = p->as; - - if(as == ASLL || as == ASRL || as == ASRA) - return C_SCON; - if(p->to.type == D_REG && p->to.reg == REGSP){ - if(as == AADD || as == ASUB){ - if(off >= 0 && off <= 508) - return C_SCON; - if(as == ASUB){ - p->as = AADD; - p->from.offset = -p->from.offset; - } - return C_LCON; - } - diag("unknown type in immcon"); - } - if(as == AADD || as == ASUB){ - if(p->reg != NREG) - return immsmall(off, p, C_SCON, C_LCON, C_GCON); - return immacon(off, p, C_SCON, C_LCON); - } - if(as == AMOVW && p->from.type == D_CONST && p->to.type == D_REG && immbuildcon(off, p)) - return C_BCON; - if(as == ACMP && p->from.type == D_CONST && immbuildcon(off, p)) - return C_BCON; - if(as == ACMP || as == AMOVW) - return immacon(off, p, C_SCON, C_LCON); - return C_LCON; -} - -int -thumbaclass(Adr *a, Prog *p) -{ - Sym *s; - int t; - - switch(a->type) { - case D_NONE: - return C_NONE; - case D_REG: - if(a->reg == REGSP) - return C_SP; - if(a->reg == REGPC) - return C_PC; - if(a->reg >= 8) - return C_HREG; - return C_REG; - case D_SHIFT: - diag("D_SHIFT in thumbaclass"); - return C_SHIFT; - case D_FREG: - diag("D_FREG in thumbaclass"); - return C_FREG; - case D_FPCR: - diag("D_FPCR in thumbaclass"); - return C_FCR; - case D_OREG: - switch(a->name) { - case D_EXTERN: - case D_STATIC: - if(a->sym == 0 || a->sym->name == 0) { - print("null sym external\n"); - print("%D\n", a); - return C_GOK; - } - t = a->sym->type; - if(t == 0 || t == SXREF) { - diag("undefined external: %s in %s\n", - a->sym->name, TNAME); - a->sym->type = SDATA; - } - instoffset = a->sym->value + a->offset; - return C_ADDR; /* INITDAT unknown at this stage */ - case D_AUTO: - instoffset = autosize + a->offset; - return immauto(instoffset, p); - case D_PARAM: - instoffset = autosize + a->offset + 4L; -// print("D_PARAM %s %d+%d+%d = %d\n", a->sym != S ? a->sym->name : "noname", autosize, a->offset, 4, autosize+a->offset+4); - return immauto(instoffset, p); - case D_NONE: - instoffset = a->offset; - if(a->reg == REGSP) - return immauto(instoffset, p); - else - return immoreg(instoffset, p); - } - return C_GOK; - case D_PSR: - diag("D_PSR in thumbaclass"); - return C_PSR; - case D_OCONST: - switch(a->name) { - case D_EXTERN: - case D_STATIC: - s = a->sym; - t = s->type; - if(t == 0 || t == SXREF) { - diag("undefined external: %s in %s\n", - s->name, TNAME); - s->type = SDATA; - } - instoffset = s->value + a->offset; - if(s->type == STEXT){ - instoffset = s->value + a->offset; -#ifdef CALLEEBX - instoffset += fnpinc(s); -#else - if(s->thumb) - instoffset++; // T bit -#endif - return C_LCON; - } - return C_LCON; /* INITDAT unknown at this stage */ - // return immcon(instoffset, p); - } - return C_GOK; - case D_FCONST: - diag("D_FCONST in thumaclass"); - return C_LFCON; - case D_CONST: - switch(a->name) { - case D_NONE: - instoffset = a->offset; - if(a->reg != NREG) - goto aconsize; - return immcon(instoffset, p); - case D_EXTERN: - case D_STATIC: - s = a->sym; - if(s == S) - break; - t = s->type; - switch(t) { - case 0: - case SXREF: - diag("undefined external: %s in %s\n", - s->name, TNAME); - s->type = SDATA; - break; - case SCONST: - case STEXT: - instoffset = s->value + a->offset; -#ifdef CALLEEBX - instoffset += fnpinc(s); -#else - if(s->thumb) - instoffset++; // T bit -#endif - return C_LCON; - } - instoffset = s->value + a->offset; - return C_LCON; /* INITDAT unknown at this stage */ - // return immcon(instoffset, p); - case D_AUTO: - instoffset = autosize + a->offset; - goto aconsize; - case D_PARAM: - instoffset = autosize + a->offset + 4L; - aconsize: - if(p->from.reg == REGSP || p->from.reg == NREG) - return instoffset >= 0 && instoffset < 1024 ? C_SACON : C_GACON; - else if(p->from.reg == p->to.reg) - return immacon(instoffset, p, C_SACON, C_GACON); - return immsmall(instoffset, p, C_SACON, C_LACON, C_GACON); - } - return C_GOK; - case D_BRANCH: { - int v, va; - - p->align = 0; - v = -4; - va = 0; - if(p->cond != P){ - v = (p->cond->pc - p->pc) - 4; - va = p->cond->pc; - } - instoffset = v; - if(p->as == AB){ - if(v >= -2048 && v <= 2046) - return C_SBRA; - p->align = 4; - instoffset = va; - return C_LBRA; - } - if(p->as == ABL){ -#ifdef CALLEEBX - int e; - - if((e = fninc(p->to.sym))) { - v += e; - va += e; - instoffset += e; - } -#endif - if(v >= -4194304 && v <= 4194302) - return C_SBRA; - p->align = 2; - instoffset = va; - return C_LBRA; - } - if(p->as == ABX){ - v = va; - if(v >= 0 && v <= 255) - return C_SBRA; - p->align = 2; - instoffset = va; - return C_LBRA; - } - if(v >= -256 && v <= 254) - return C_SBRA; - if(v >= -(2048-2) && v <= (2046+2)) - return C_LBRA; - p->align = 2; - instoffset = va; - return C_GBRA; - } - } - return C_GOK; -} - -// as a1 a2 a3 type size param lit vers -Optab thumboptab[] = -{ - { ATEXT, C_ADDR, C_NONE, C_LCON, 0, 0, 0 }, - { ATEXT, C_ADDR, C_REG, C_LCON, 0, 0, 0 }, - { AMVN, C_REG, C_NONE, C_REG, 1, 2, 0 }, - { ASRL, C_REG, C_NONE, C_REG, 1, 2, 0 }, - { ACMP, C_REG, C_REG, C_NONE, 1, 2, 0 }, - { ACMN, C_REG, C_REG, C_NONE, 1, 2, 0 }, - { AADD, C_REG, C_REG, C_REG, 2, 2, 0 }, - { AADD, C_REG, C_NONE, C_REG, 2, 2, 0 }, - { AADD, C_SCON, C_REG, C_REG, 3, 2, 0 }, - { AADD, C_LCON, C_REG, C_REG, 49, 4, 0 }, - { AADD, C_GCON, C_REG, C_REG, 36, 4, 0, LFROM }, - // { AADD, C_LCON, C_NONE, C_REG, 3, 2, 0, LFROM }, - { ASRL, C_SCON, C_REG, C_REG, 4, 2, 0 }, - { ASRL, C_SCON, C_NONE, C_REG, 4, 2, 0 }, - { AADD, C_SCON, C_NONE, C_REG, 5, 2, 0 }, - { AADD, C_LCON, C_NONE, C_REG, 37, 4, 0, LFROM }, - { ACMP, C_SCON, C_REG, C_NONE, 5, 2, 0 }, - { ACMP, C_BCON, C_REG, C_NONE, 48, 6, 0 }, - { ACMP, C_LCON, C_REG, C_NONE, 39, 4, 0, LFROM }, - { AMOVW, C_SCON, C_NONE, C_REG, 5, 2, 0 }, - { AMOVW, C_BCON, C_NONE, C_REG, 47, 4, 0 }, - { AMOVW, C_LCON, C_NONE, C_REG, 38, 2, 0, LFROM }, - // { AADD, C_LCON, C_PC, C_REG, 6, 2, 0, LFROM }, - // { AADD, C_LCON, C_SP, C_REG, 6, 2, 0, LFROM }, - { AADD, C_SCON, C_NONE, C_SP, 7, 2, 0 }, - { AADD, C_LCON, C_NONE, C_SP, 40, 4, 0, LFROM }, - { AADD, C_REG, C_NONE, C_HREG, 8, 2, 0 }, - { AADD, C_HREG, C_NONE, C_REG, 8, 2, 0 }, - { AADD, C_HREG, C_NONE, C_HREG, 8, 2, 0 }, - { AMOVW, C_REG, C_NONE, C_HREG, 8, 2, 0 }, - { AMOVW, C_HREG, C_NONE, C_REG, 8, 2, 0 }, - { AMOVW, C_HREG, C_NONE, C_HREG, 8, 2, 0 }, - { ACMP, C_REG, C_HREG, C_NONE, 8, 2, 0 }, - { ACMP, C_HREG, C_REG, C_NONE, 8, 2, 0 }, - { ACMP, C_HREG, C_HREG, C_NONE, 8, 2, 0 }, - { AB, C_NONE, C_NONE, C_SBRA, 9, 2, 0, LPOOL }, - { ABEQ, C_NONE, C_NONE, C_SBRA, 10, 2, 0 }, - { ABL, C_NONE, C_NONE, C_SBRA, 11, 4, 0 }, - { ABX, C_NONE, C_NONE, C_SBRA, 12, 10, 0 }, - { AB, C_NONE, C_NONE, C_LBRA, 41, 8, 0, LPOOL }, - { ABEQ, C_NONE, C_NONE, C_LBRA, 46, 4, 0 }, - { ABL, C_NONE, C_NONE, C_LBRA, 43, 14, 0 }, - { ABX, C_NONE, C_NONE, C_LBRA, 44, 14, 0 }, - { ABEQ, C_NONE, C_NONE, C_GBRA, 42, 10, 0 }, - // { AB, C_NONE, C_NONE, C_SOREG, 13, 0, 0 }, - // { ABL, C_NONE, C_NONE, C_SOREG, 14, 0, 0 }, - { ABL, C_NONE, C_NONE, C_REG, 51, 4, 0 }, - { ABX, C_NONE, C_NONE, C_REG, 15, 8, 0 }, - { ABX, C_NONE, C_NONE, C_HREG, 15, 8, 0 }, - { ABXRET, C_NONE, C_NONE, C_REG, 45, 2, 0 }, - { ABXRET, C_NONE, C_NONE, C_HREG, 45, 2, 0 }, - { ASWI, C_NONE, C_NONE, C_LCON, 16, 2, 0 }, - { AWORD, C_NONE, C_NONE, C_LCON, 17, 4, 0 }, - { AWORD, C_NONE, C_NONE, C_GCON, 17, 4, 0 }, - { AWORD, C_NONE, C_NONE, C_ADDR, 17, 4, 0 }, - { ADWORD, C_LCON, C_NONE, C_LCON, 50, 8, 0 }, - { AMOVW, C_SAUTO, C_NONE, C_REG, 18, 2, REGSP }, - { AMOVW, C_LAUTO, C_NONE, C_REG, 33, 6, 0, LFROM }, - // { AMOVW, C_OFFPC, C_NONE, C_REG, 18, 2, REGPC, LFROM }, - { AMOVW, C_SOREG, C_NONE, C_REG, 19, 2, 0 }, - { AMOVHU, C_SOREG, C_NONE, C_REG, 19, 2, 0 }, - { AMOVBU, C_SOREG, C_NONE, C_REG, 19, 2, 0 }, - { AMOVW, C_REG, C_NONE, C_SAUTO, 20, 2, 0 }, - { AMOVW, C_REG, C_NONE, C_LAUTO, 34, 6, 0, LTO }, - { AMOVW, C_REG, C_NONE, C_SOREG, 21, 2, 0 }, - { AMOVH, C_REG, C_NONE, C_SOREG, 21, 2, 0 }, - { AMOVB, C_REG, C_NONE, C_SOREG, 21, 2, 0 }, - { AMOVHU, C_REG, C_NONE, C_SOREG, 21, 2, 0 }, - { AMOVBU, C_REG, C_NONE, C_SOREG, 21, 2, 0 }, - { AMOVW, C_REG, C_NONE, C_REG, 22, 2, 0 }, - { AMOVB, C_REG, C_NONE, C_REG, 23, 4, 0 }, - { AMOVH, C_REG, C_NONE, C_REG, 23, 4, 0 }, - { AMOVBU, C_REG, C_NONE, C_REG, 23, 4, 0 }, - { AMOVHU, C_REG, C_NONE, C_REG, 23, 4, 0 }, - { AMOVH, C_SOREG, C_NONE, C_REG, 24, 4, 0 }, - { AMOVB, C_SOREG, C_NONE, C_REG, 24, 4, 0 }, - { AMOVW, C_SACON, C_NONE, C_REG, 25, 2, 0 }, - { AMOVW, C_LACON, C_NONE, C_REG, 35, 4, 0 }, - { AMOVW, C_GACON, C_NONE, C_REG, 35, 4, 0, LFROM }, - { AMOVM, C_LCON, C_NONE, C_REG, 26, 2, 0 }, - { AMOVM, C_REG, C_NONE, C_LCON, 27, 2, 0 }, - { AMOVW, C_LOREG, C_NONE, C_REG, 28, 4, 0 }, - { AMOVH, C_LOREG, C_NONE, C_REG, 28, 4, 0 }, - { AMOVB, C_LOREG, C_NONE, C_REG, 28, 4, 0 }, - { AMOVHU, C_LOREG, C_NONE, C_REG, 28, 4, 0 }, - { AMOVBU, C_LOREG, C_NONE, C_REG, 28, 4, 0 }, - { AMOVW, C_REG, C_NONE, C_LOREG, 29, 4, 0 }, - { AMOVH, C_REG, C_NONE, C_LOREG, 29, 4, 0 }, - { AMOVB, C_REG, C_NONE, C_LOREG, 29, 4, 0 }, - { AMOVHU, C_REG, C_NONE, C_LOREG, 29, 4, 0 }, - { AMOVBU, C_REG, C_NONE, C_LOREG, 29, 4, 0 }, - { AMOVW, C_GOREG, C_NONE, C_REG, 28, 4, 0, LFROM }, - { AMOVH, C_GOREG, C_NONE, C_REG, 28, 4, 0, LFROM }, - { AMOVB, C_GOREG, C_NONE, C_REG, 28, 4, 0, LFROM }, - { AMOVHU, C_GOREG, C_NONE, C_REG, 28, 4, 0, LFROM }, - { AMOVBU, C_GOREG, C_NONE, C_REG, 28, 4, 0, LFROM }, - { AMOVW, C_REG, C_NONE, C_GOREG, 29, 4, 0, LTO }, - { AMOVH, C_REG, C_NONE, C_GOREG, 29, 4, 0, LTO }, - { AMOVB, C_REG, C_NONE, C_GOREG, 29, 4, 0, LTO }, - { AMOVHU, C_REG, C_NONE, C_GOREG, 29, 4, 0, LTO }, - { AMOVBU, C_REG, C_NONE, C_GOREG, 29, 4, 0, LTO }, - { AMOVW, C_ADDR, C_NONE, C_REG, 30, 4, 0, LFROM }, - { AMOVH, C_ADDR, C_NONE, C_REG, 32, 6, 0, LFROM }, - { AMOVB, C_ADDR, C_NONE, C_REG, 32, 6, 0, LFROM }, - { AMOVHU, C_ADDR, C_NONE, C_REG, 30, 4, 0, LFROM }, - { AMOVBU, C_ADDR, C_NONE, C_REG, 30, 4, 0, LFROM }, - { AMOVW, C_REG, C_NONE, C_ADDR, 31, 4, 0, LTO }, - { AMOVH, C_REG, C_NONE, C_ADDR, 31, 4, 0, LTO }, - { AMOVB, C_REG, C_NONE, C_ADDR, 31, 4, 0, LTO }, - { AMOVHU, C_REG, C_NONE, C_ADDR, 31, 4, 0, LTO }, - { AMOVBU, C_REG, C_NONE, C_ADDR, 31, 4, 0, LTO }, - - { AXXX, C_NONE, C_NONE, C_NONE, 0, 2, 0 }, -}; - -#define OPCNTSZ 52 -int opcount[OPCNTSZ]; - -// is this too pessimistic ? -int -brextra(Prog *p) -{ - int c; - - // +2 is for padding - if(p->as == ATEXT) - return 0-0+2; - if(!isbranch(p)) - diag("bad op in brextra()"); - c = thumbaclass(&p->to, p); - switch(p->as){ - case AB: - if(c != C_SBRA) - return 0; - return 8-2+2; - case ABL: - if(c != C_SBRA) - return 0; - return 14-4+2; - case ABX: - if(c == C_REG || c == C_HREG) - return 0; -#ifdef CALLEEBX - diag("ABX $I in brextra"); -#endif - if(c != C_SBRA) - return 0; - return 14-10+2; - default: - if(c == C_GBRA) - return 0; - if(c == C_LBRA) - return 10-4+2; - return 10-2+2; - } - return 0; -} - -#define high(r) ((r)>=8) - -static int32 -mv(Prog *p, int r, int off) -{ - int v, o; - if(p != nil && p->cond != nil){ // in literal pool - v = p->cond->pc - p->pc - 4; - if(p->cond->pc & 3) - diag("mv: bad literal pool alignment"); - if(v & 3) - v += 2; // ensure M(4) offset - mult(p, v, 4); - off = v/4; - numr(p, off, 0, 255); - o = 0x9<<11; - } - else{ - numr(p, off, 0, 255); - o = 0x4<<11; - } - o |= (r<<8) | off; - return o; -} - -static void -mvcon(Prog *p, int r, int c, int32 *o1, int32 *o2) -{ - int op = 0, n = 0; - - if(c >= 0 && c <= 255) - diag("bad c in mvcon"); - if(c >= -255 && c < 0) // mv, neg - c = -c; - else if(c >= 256 && c <= 510){ // mv, add - n = rand()%(511-c) + (c-255); - c -= n; - // n = c-255; - // c = 255; - op = AADD; - } - else{ - if(c < 0) - diag("-ve in mvcon"); - while(!(c & 1)){ - n++; - c >>= 1; - } - if(c >= 0 && c <= 255) // mv, lsl - op = ASLL; - else - diag("bad shift in mvcon"); - } - *o1 = mv(p, r, c); - switch(op){ - case 0: - *o2 = (1<<14) | (9<<6) | (r<<3) | r; - break; - case AADD: - *o2 = (6<<11) | (r<<8) | n; - break; - case ASLL: - *o2 = (n<<6) | (r<<3) | r; - break; - } -} - -static int32 -mvlh(int rs, int rd) -{ - int o = 0x46<<8; - - if(high(rs)){ - rs -= 8; - o |= 1<<6; - } - if(high(rd)){ - rd -= 8; - o |= 1<<7; - } - o |= (rs<<3) | rd; - return o; -} - -void -thumbbuildop() -{ - int i, n, r; - Optab *optab = thumboptab; - Oprang *oprange = thumboprange; - - for(n=0; optab[n].as != AXXX; n++) - ; - qsort(optab, n, sizeof(optab[0]), ocmp); - for(i=0; i<n; i++) { - r = optab[i].as; - oprange[r].start = optab+i; - while(optab[i].as == r) - i++; - oprange[r].stop = optab+i; - i--; - - switch(r) - { - default: - break; - case ABEQ: - oprange[ABNE] = oprange[r]; - oprange[ABCS] = oprange[r]; - oprange[ABHS] = oprange[r]; - oprange[ABCC] = oprange[r]; - oprange[ABLO] = oprange[r]; - oprange[ABMI] = oprange[r]; - oprange[ABPL] = oprange[r]; - oprange[ABVS] = oprange[r]; - oprange[ABVC] = oprange[r]; - oprange[ABHI] = oprange[r]; - oprange[ABLS] = oprange[r]; - oprange[ABGE] = oprange[r]; - oprange[ABLT] = oprange[r]; - oprange[ABGT] = oprange[r]; - oprange[ABLE] = oprange[r]; - break; - case AMVN: - oprange[AADC] = oprange[r]; - oprange[ASBC] = oprange[r]; - oprange[AMUL] = oprange[r]; - oprange[AAND] = oprange[r]; - oprange[AEOR] = oprange[r]; - oprange[AORR] = oprange[r]; - oprange[ABIC] = oprange[r]; - oprange[AMULU] = oprange[r]; - break; - case ACMN: - oprange[ATST] = oprange[r]; - break; - case ASRL: - oprange[ASRA] = oprange[r]; - oprange[ASLL] = oprange[r]; - break; - case AADD: - oprange[ASUB] = oprange[r]; - break; - } - } -} - -void -thumbasmout(Prog *p, Optab *o) -{ - int32 o1, o2, o3, o4, o5, o6, o7, v; - int r, rf, rt; - - rf = p->from.reg; - rt = p->to.reg; - r = p->reg; - o1 = o2 = o3 = o4 = o5 = o6 = o7 = 0; -if(debug['P']) print("%ux: %P type %d %d\n", (uint32)(p->pc), p, o->type, p->align); - opcount[o->type] += o->size; - switch(o->type) { - default: - diag("unknown asm %d", o->type); - prasm(p); - break; - case 0: /* pseudo ops */ -if(debug['G']) print("%ux: %s: thumb\n", (uint32)(p->pc), p->from.sym->name); - break; - case 1: /* op R, -, R or op R, R, - */ - o1 = thumboprr(p->as); - if(rt == NREG) - rt = r; - lowreg(p, rf); - lowreg(p, rt); - o1 |= (0x10<<10) | (rf<<3) | rt; - break; - case 2: /* add/sub R, R, R or add/sub R, -, R */ - o1 = p->as == AADD ? 0x0<<9 : 0x1<<9; - if(r == NREG) - r = rt; - lowreg(p, rf); - lowreg(p, r); - lowreg(p, rt); - o1 |= (0x6<<10) | (rf<<6) | (r<<3) | rt; - break; - case 3: /* add/sub $I, R, R or add/sub $I, -, R */ - thumbaclass(&p->from, p); - o1 = p->as == AADD ? 0x0<<9 : 0x1<<9; - if(r == NREG) - r = rt; - numr(p, instoffset, 0, 7); - lowreg(p, r); - lowreg(p, rt); - o1 |= (0x7<<10) | (instoffset<<6) | (r<<3) | rt; - break; - case 4: /* shift $I, R, R or shift $I, -, R */ - thumbaclass(&p->from, p); - if(instoffset < 0) - diag("negative shift in thumbasmout"); - instoffset %= 32; - o1 = thumbopri(p->as); - if(r == NREG) - r = rt; - numr(p, instoffset, 0, 31); - lowreg(p, r); - lowreg(p, rt); - o1 |= (0x0<<13) | (instoffset<<6) | (r<<3) | rt; - break; - case 5: /* add/sub/mov $I, -, R or cmp $I, R, - */ - thumbaclass(&p->from, p); - o1 = thumbopri(p->as); - if(rt == NREG) - rt = r; - numr(p, instoffset, 0, 255); - lowreg(p, rt); - o1 |= (0x1<<13) | (rt<<8) | instoffset; - break; - case 6: /* add $I, PC/SP, R */ - if(p->as == ASUB) - diag("subtract in add $I, PC/SP, R"); - thumbaclass(&p->from, p); - o1 = r == REGSP ? 0x1<<11 : 0x0<<11; - numr(p, instoffset, 0, 255); - regis(p, r, REGSP, REGPC); - lowreg(p, rt); - o1 |= (0xa<<12) | (rt<<8) | instoffset; - break; - case 7: /* add, sub $I, SP */ - thumbaclass(&p->from, p); - o1 = p->as == AADD ? 0x0<<7 : 0x1<<7; - numr(p, instoffset, 0, 508); - mult(p, instoffset, 4); - regis(p, rt, REGSP, REGSP); - o1 |= (0xb0<<8) | (instoffset>>2); - break; - case 8: /* add/mov/cmp R, R where at least 1 reg is high */ - o1 = 0; - if(rt == NREG) - rt = r; - if(high(rf)){ - o1 |= 1<<6; - rf -= 8; - } - if(high(rt)){ - o1 |= 2<<6; - rt -= 8; - } - if(o1 == 0) - diag("no high register(%P)", p); - o1 |= thumbophh(p->as); - o1 |= (0x11<<10) | (rf<<3) | rt; - break; - case 9: /* B $I */ - thumbaclass(&p->to, p); - numr(p, instoffset, -2048, 2046); - o1 = (0x1c<<11) | ((instoffset>>1)&0x7ff); - break; - case 10: /* Bcc $I */ - thumbaclass(&p->to, p); - numr(p, instoffset, -256, 254); - o1 = thumbopbra(p->as); - o1 |= (0xd<<12) | ((instoffset>>1)&0xff); - break; - case 11: /* BL $I */ - thumbaclass(&p->to, p); - numr(p, instoffset, -4194304, 4194302); - o1 = (0x1e<<11) | ((instoffset>>12)&0x7ff); - o2 = (0x1f<<11) | ((instoffset>>1)&0x7ff); - break; - case 12: /* BX $I */ -#ifdef CALLEEBX - diag("BX $I case"); -#endif - thumbaclass(&p->to, p); - if(p->to.sym->thumb) - instoffset |= 1; // T bit - o1 = mvlh(REGPC, REGTMPT); - o2 = (0x6<<11) | (REGTMPT<<8) | 7; // add 7, RTMP (T bit + PC offset) - o3 = mvlh(REGTMPT, REGLINK); - o4 = mv(nil, REGTMPT, instoffset); - o5 = (0x11c<<6) | (REGTMPT<<3); - // o1 = mv(nil, REGTMPT, v); - // o2 = (0x11b<<6) | (REGPC<<3) | REGLINK; - // o3 = (0x11c<<6) | (REGTMPT<<3); - break; - case 13: /* B O(R) */ - diag("B O(R)"); - break; - case 14: /* BL O(R) */ - diag("BL O(R)"); - break; - case 15: /* BX R */ - o1 = mvlh(REGPC, REGTMPT); - o2 = (0x6<<11) | (REGTMPT<<8) | 5; // add 5, RTMP (T bit + PC offset) - o3 = mvlh(REGTMPT, REGLINK); - o4 = 0; - if(high(rt)){ - rt -= 8; - o4 |= 1<<6; - } - o4 |= (0x8e<<7) | (rt<<3); - // o1 = (0x11c<<6) | (rt<<3); - break; - case 16: /* SWI $I */ - thumbaclass(&p->to, p); - numr(p, instoffset, 0, 255); - o1 = (0xdf<<8) | instoffset; - break; - case 17: /* AWORD */ - thumbaclass(&p->to, p); - o1 = instoffset&0xffff; - o2 = (instoffset>>16)&0xffff; - break; - case 18: /* AMOVW O(SP), R and AMOVW O(PC), R */ - thumbaclass(&p->from, p); - rf = o->param; - o1 = rf == REGSP ? 0x13<<11 : 0x9<<11; - regis(p, rf, REGSP, REGPC); - lowreg(p, rt); - mult(p, instoffset, 4); - numr(p, instoffset/4, 0, 255); - o1 |= (rt<<8) | (instoffset/4); - break; - case 19: /* AMOVW... O(R), R */ - thumbaclass(&p->from, p); - o1 = thumbopmv(p->as, 1); - v = 4; - if(p->as == AMOVHU) - v = 2; - else if(p->as == AMOVBU) - v = 1; - mult(p, instoffset, v); - lowreg(p, rf); - lowreg(p, rt); - numr(p, instoffset/v, 0, 31); - o1 |= ((instoffset/v)<<6) | (rf<<3) | rt; - break; - case 20: /* AMOVW R, O(SP) */ - thumbaclass(&p->to, p); - o1 = 0x12<<11; - if(rt != NREG) regis(p, rt, REGSP, REGSP); - lowreg(p, rf); - mult(p, instoffset, 4); - numr(p, instoffset/4, 0, 255); - o1 |= (rf<<8) | (instoffset/4); - break; - case 21: /* AMOVW... R, O(R) */ - thumbaclass(&p->to, p); - o1 = thumbopmv(p->as, 0); - v = 4; - if(p->as == AMOVHU || p->as == AMOVH) - v = 2; - else if(p->as == AMOVBU || p->as == AMOVB) - v = 1; - lowreg(p, rf); - lowreg(p, rt); - mult(p, instoffset, v); - numr(p, instoffset/v, 0, 31); - o1 |= ((instoffset/v)<<6) | (rt<<3) | rf; - break; - case 22: /* AMOVW R, R -> ASLL $0, R, R */ - o1 = thumbopri(ASLL); - lowreg(p, rf); - lowreg(p, rt); - o1 |= (0x0<<13) | (rf<<3) | rt; - break; - case 23: /* AMOVB/AMOVH/AMOVBU/AMOVHU R, R */ - o1 = thumbopri(ASLL); - o2 = p->as == AMOVB || p->as == AMOVH ? thumbopri(ASRA) : thumbopri(ASRL); - v = p->as == AMOVB || p->as == AMOVBU ? 24 : 16; - lowreg(p, rf); - lowreg(p, rt); - o1 |= (0x0<<13) | (v<<6) | (rf<<3) | rt; - o2 |= (0x0<<13) | (v<<6) | (rt<<3) | rt; - break; - case 24: /* AMOVH/AMOVB O(R), R -> AMOVH/AMOVB [R, R], R */ - thumbaclass(&p->from, p); - lowreg(p, rf); - lowreg(p, rt); - if(rf == rt) - r = REGTMPT; - else - r = rt; - if(p->as == AMOVB) - numr(p, instoffset, 0, 31); - else{ - mult(p, instoffset, 2); - numr(p, instoffset, 0, 62); - } - o1 = mv(p, r, instoffset); - o2 = p->as == AMOVH ? 0x2f<<9 : 0x2b<<9; - o2 |= (r<<6) | (rf<<3) | rt; - break; - case 25: /* MOVW $sacon, R */ - thumbaclass(&p->from, p); -// print("25: %d %d %d %d\n", instoffset, rf, r, rt); - if(rf == NREG) - rf = REGSP; - lowreg(p, rt); - if(rf == REGSP){ - mult(p, instoffset, 4); - numr(p, instoffset>>2, 0, 255); - o1 = (0x15<<11) | (rt<<8) | (instoffset>>2); // add $O, SP, R - } - else if(rf == rt){ - numr(p, instoffset, 0, 255); - o1 = (0x6<<11) | (rt<<8) | instoffset; // add $O, R - } - else{ - lowreg(p, rf); - numr(p, instoffset, 0, 7); - o1 = (0xe<<9) | (instoffset<<6) | (rf<<3) | rt; // add $O, Rs, Rd - } - break; - case 26: /* AMOVM $c, oreg -> stmia */ - lowreg(p, rt); - numr(p, p->from.offset, -256, 255); - o1 = (0x18<<11) | (rt<<8) | (p->from.offset&0xff); - break; - case 27: /* AMOVM oreg, $c ->ldmia */ - lowreg(p, rf); - numr(p, p->to.offset, -256, 256); - o1 = (0x19<<11) | (rf<<8) | (p->to.offset&0xff); - break; - case 28: /* AMOV* O(R), R -> AMOV* [R, R], R (offset large) */ - thumbaclass(&p->from, p); - lowreg(p, rf); - lowreg(p, rt); - if(rf == rt) - r = REGTMPT; - else - r = rt; - o1 = mv(p, r, instoffset); - o2 = thumboprrr(p->as, 1); - o2 |= (r<<6) | (rf<<3) | rt; - break; - case 29: /* AMOV* R, O(R) -> AMOV* R, [R, R] (offset large) */ - thumbaclass(&p->to, p); - lowreg(p, rf); - lowreg(p, rt); - if(rt == REGTMPT){ // used as tmp reg - if(instoffset >= 0 && instoffset <= 255){ - o1 = (1<<13) | (2<<11) | (rt<<8) | instoffset; // add $O, R7 - o2 = thumbopirr(p->as, 0); - o2 |= (0<<6) | (rt<<3) | rf; // mov* R, 0(R) - } - else - diag("big offset - case 29"); - } - else{ - o1 = mv(p, REGTMPT, instoffset); - o2 = thumboprrr(p->as, 0); - o2 |= (REGTMPT<<6) | (rt<<3) | rf; - } - break; - case 30: /* AMOVW... *addr, R */ - diag("likely broken"); // does this still refer to SB? - thumbaclass(&p->from, p); - o1 = mv(p, rt, instoffset); // MOV addr, rtmp - o2 = thumbopmv(p->as, 1); - lowreg(p, rt); - o2 |= (rt<<3) | rt; // MOV* 0(rtmp), R - break; - case 31: /* AMOVW... R, *addr */ - diag("likely broken"); // does this still refer to SB? - thumbaclass(&p->to, p); - o1 = mv(p, REGTMPT, instoffset); - o2 = thumbopmv(p->as, 0); - lowreg(p, rf); - o2 |= (REGTMPT<<3) | rf; - break; - case 32: /* AMOVH/AMOVB *addr, R -> AMOVH/AMOVB [R, R], R */ - thumbaclass(&p->from, p); - o1 = mv(p, rt, instoffset); - lowreg(p, rt); - o2 = mv(nil, REGTMPT, 0); - o3 = p->as == AMOVH ? 0x2f<<9 : 0x2b<<9; - o3 |= (REGTMPT<<6) | (rt<<3) | rt; - break; - case 33: /* AMOVW O(SP), R (O large) */ - thumbaclass(&p->from, p); - lowreg(p, rt); - o1 = mv(p, rt, instoffset); - o2 = (0x111<<6) | (REGSP-8)<<3 | rt; // add SP, rt - o3 = thumbopmv(p->as, 1); - o3 |= (rt<<3) | rt; - break; - case 34: /* AMOVW R, O(SP) (O large) */ - thumbaclass(&p->to, p); - lowreg(p, rf); - o1 = mv(p, REGTMPT, instoffset); - o2 = (0x111<<6) | (REGSP-8)<<3 | REGTMPT; // add SP, REGTMP - o3 = thumbopmv(p->as, 0); - o3 |= (REGTMPT<<3) | rf; - break; - case 35: /* AMOVW $lacon, R */ - thumbaclass(&p->from, p); - lowreg(p, rt); - if(rf == NREG) - rf = REGSP; - if(rf == rt) - rf = r = REGTMPT; - else - r = rt; -// print("35: io=%d rf=%d rt=%d\n", instoffset, rf, rt); - o1 = mv(p, r, instoffset); // mov O, Rd - if(high(rf)) - o2 = (0x44<<8) | (0x1<<6) | ((rf-8)<<3) | rt; // add Rs, Rd - else - o2 = (0x6<<10) | (rf<<6) | (rt<<3) | rt; // add Rs, Rd - break; - case 36: /* AADD/ASUB $i, r, r when $i too big */ - thumbaclass(&p->from, p); - lowreg(p, r); - lowreg(p, rt); - o1 = mv(p, REGTMPT, instoffset); - o2 = p->as == AADD ? 0xc<<9 : 0xd<<9; - o2 |= (REGTMPT<<6) | (r<<3) | rt; - break; - case 37: /* AADD/ASUB $i, r when $i too big */ - thumbaclass(&p->from, p); - lowreg(p, rt); - o1 = mv(p, REGTMPT, instoffset); - o2 = p->as == AADD ? 0xc<<9 : 0xd<<9; - o2 |= (REGTMPT<<6) | (rt<<3) | rt; - break; - case 38: /* AMOVW $i, r when $i too big */ - thumbaclass(&p->from, p); - lowreg(p, rt); - o1 = mv(p, rt, instoffset); - break; - case 39: /* ACMP $i, r when $i too big */ - thumbaclass(&p->from, p); - lowreg(p, r); - o1 = mv(p, REGTMPT, instoffset); - o2 = (0x10a<<6) | (REGTMPT<<3) | r; - break; - case 40: /* add, sub $I, SP when $I large*/ - thumbaclass(&p->from, p); - if(p->as == ASUB) - instoffset = -instoffset; - o1 = mv(p, REGTMPT, instoffset); - o2 = (0x112<<6) | (REGTMPT<<3) | (REGSP-8); - regis(p, rt, REGSP, REGSP); - break; - case 41: /* BL LBRA */ - thumbaclass(&p->to, p); - o1 = (0x9<<11) | (REGTMPT<<8); // mov 0(pc), r7 - o2 = mvlh(REGTMPT, REGPC); // mov r7, pc - o3 = instoffset&0xffff; // $lab - o4 = (instoffset>>16)&0xffff; - break; - case 42: /* Bcc GBRA */ - thumbaclass(&p->to, p); - o1 = (0xd<<12) | thumbopbra(relinv(p->as)) | (6>>1); // bccnot - // ab lbra - o2 = (0x9<<11) | (REGTMPT<<8); // mov 0(pc), r7 - o3 = mvlh(REGTMPT, REGPC); // mov r7, pc - o4 = instoffset&0xffff; // $lab - o5 = (instoffset>>16)&0xffff; - break; - case 43: /* BL LBRA */ - thumbaclass(&p->to, p); - o1 = mvlh(REGPC, REGTMPT); // mov pc, r7 - o2 = (0x6<<11) | (REGTMPT<<8) | 10; // add 10, r7 - o3 = mvlh(REGTMPT, REGLINK); // mov r7, lr - o4 = (0x9<<11) | (REGTMPT<<8); // mov o(pc), r7 - o5 = mvlh(REGTMPT, REGPC); // mov r7, pc - o6 = instoffset&0xffff; // $lab - o7 = (instoffset>>16)&0xffff; - break; - case 44: /* BX LBRA */ -#ifdef CALLEEBX - diag("BX LBRA case"); -#endif - thumbaclass(&p->to, p); - if(p->to.sym->thumb) - instoffset |= 1; // T bit - o1 = mvlh(REGPC, REGTMPT); // mov pc, r7 - o2 = (0x6<<11) | (REGTMPT<<8) | 11; // add 11, r7 - o3 = mvlh(REGTMPT, REGLINK); // mov r7, lr - o4 = (0x9<<11) | (REGTMPT<<8); // mov o(pc), r7 - o5 = (0x11c<<6) | (REGTMPT<<3); // bx r7 - o6 = instoffset&0xffff; // $lab - o7 = (instoffset>>16)&0xffff; - break; - case 45: /* BX R when returning from fn */ - o1 = 0; - if(high(rt)){ - rt -= 8; - o1 |= 1<<6; - } - o1 |= (0x8e<<7) | (rt<<3); - break; - case 46: /* Bcc LBRA */ - thumbaclass(&p->to, p); - o1 = (0xd<<12) | thumbopbra(relinv(p->as)) | (0>>1); // bccnot - // ab lbra - instoffset -= 2; - numr(p, instoffset, -2048, 2046); - o2 = (0x1c<<11) | ((instoffset>>1)&0x7ff); - break; - case 47: /* mov $i, R where $i can be built */ - thumbaclass(&p->from, p); - mvcon(p, rt, instoffset, &o1, &o2); - break; - case 48: /* ACMP $i, r when $i built up */ - thumbaclass(&p->from, p); - lowreg(p, r); - mvcon(p, REGTMPT, instoffset, &o1, &o2); - o3 = (0x10a<<6) | (REGTMPT<<3) | r; - break; - case 49: /* AADD $i, r, r when $i is between 0 and 255 - could merge with case 36 */ - thumbaclass(&p->from, p); - lowreg(p, r); - lowreg(p, rt); - numr(p, instoffset, 0, 255); - o1 = mv(p, REGTMPT, instoffset); - o2 = p->as == AADD ? 0xc<<9 : 0xd<<9; - o2 |= (REGTMPT<<6) | (r<<3) | rt; - break; - case 50: /* ADWORD */ - thumbaclass(&p->from, p); - o1 = instoffset&0xffff; - o2 = (instoffset>>16)&0xffff; - thumbaclass(&p->to, p); - o3 = instoffset&0xffff; - o4 = (instoffset>>16)&0xffff; - break; - case 51: /* BL r */ - o1 = mvlh(REGPC, REGLINK); // mov pc, lr - o2 = mvlh(rt, REGPC); // mov r, pc - break; - } - - v = p->pc; - switch(o->size) { - default: - if(debug['a']) - Bprint(&bso, " %.8ux:\t\t%P\n", v, p); - break; - case 2: - if(debug['a']) - Bprint(&bso, " %.8ux: %.8ux\t%P\n", v, o1, p); - hputl(o1); - break; - case 4: - if(debug['a']) - Bprint(&bso, " %.8ux: %.8ux %.8ux\t%P\n", v, o1, o2, p); - hputl(o1); - hputl(o2); - break; - case 6: - if(debug['a']) - Bprint(&bso, "%.8ux: %.8ux %.8ux %.8ux\t%P\n", v, o1, o2, o3, p); - hputl(o1); - hputl(o2); - hputl(o3); - break; - case 8: - if(debug['a']) - Bprint(&bso, "%.8ux: %.8ux %.8ux %.8ux %.8ux\t%P\n", v, o1, o2, o3, o4, p); - hputl(o1); - hputl(o2); - hputl(o3); - hputl(o4); - break; - case 10: - if(debug['a']) - Bprint(&bso, "%.8ux: %.8ux %.8ux %.8ux %.8ux %.8ux\t%P\n", v, o1, o2, o3, o4, o5, p); - hputl(o1); - hputl(o2); - hputl(o3); - hputl(o4); - hputl(o5); - break; - case 12: - if(debug['a']) - Bprint(&bso, "%.8ux: %.8ux %.8ux %.8ux %.8ux %.8ux %.8ux\t%P\n", v, o1, o2, o3, o4, o5, o6, p); - hputl(o1); - hputl(o2); - hputl(o3); - hputl(o4); - hputl(o5); - hputl(o6); - break; - case 14: - if(debug['a']) - Bprint(&bso, "%.8ux: %.8ux %.8ux %.8ux %.8ux %.8ux %.8ux %.8ux\t%P\n", v, o1, o2, o3, o4, o5, o6, o7, p); - hputl(o1); - hputl(o2); - hputl(o3); - hputl(o4); - hputl(o5); - hputl(o6); - hputl(o7); - break; - } - if(debug['G']){ - if(o->type == 17){ - print("%x: word %d\n", p->pc, (o2<<16)+o1); - return; - } - if(o->type == 50){ - print("%x: word %d\n", p->pc, (o2<<16)+o1); - print("%x: word %d\n", p->pc, (o4<<16)+o3); - return; - } - if(o->size > 0) dis(o1, p->pc); - if(o->size > 2) dis(o2, p->pc+2); - if(o->size > 4) dis(o3, p->pc+4); - if(o->size > 6) dis(o4, p->pc+6); - if(o->size > 8) dis(o5, p->pc+8); - if(o->size > 10) dis(o6, p->pc+10); - if(o->size > 12) dis(o7, p->pc+12); - // if(o->size > 14) dis(o8, p->pc+14); - } -} - -static int32 -thumboprr(int a) -{ - switch(a) { - case AMVN: return 0xf<<6; - case ACMP: return 0xa<<6; - case ACMN: return 0xb<<6; - case ATST: return 0x8<<6; - case AADC: return 0x5<<6; - case ASBC: return 0x6<<6; - case AMUL: - case AMULU: return 0xd<<6; - case AAND: return 0x0<<6; - case AEOR: return 0x1<<6; - case AORR: return 0xc<<6; - case ABIC: return 0xe<<6; - case ASRL: return 0x3<<6; - case ASRA: return 0x4<<6; - case ASLL: return 0x2<<6; - } - diag("bad thumbop oprr %d", a); - prasm(curp); - return 0; -} - -static int32 -thumbopirr(int a, int ld) -{ - if(ld) - diag("load in thumbopirr"); - switch(a){ - case AMOVW: return 0xc<<11; - case AMOVH: - case AMOVHU: return 0x10<<11; - case AMOVB: - case AMOVBU: return 0xe<<11; - } - return 0; -} - -static int32 -thumboprrr(int a, int ld) -{ - if(ld){ - switch(a){ - case AMOVW: return 0x2c<<9; - case AMOVH: return 0x2f<<9; - case AMOVB: return 0x2b<<9; - case AMOVHU: return 0x2d<<9; - case AMOVBU: return 0x2e<<9; - } - } - else{ - switch(a){ - case AMOVW: return 0x28<<9; - case AMOVHU: - case AMOVH: return 0x29<<9; - case AMOVBU: - case AMOVB: return 0x2a<<9; - } - } - diag("bad thumbop oprrr %d", a); - prasm(curp); - return 0; -} - -static int32 -thumbopri(int a) -{ - switch(a) { - case ASRL: return 0x1<<11; - case ASRA: return 0x2<<11; - case ASLL: return 0x0<<11; - case AADD: return 0x2<<11; - case ASUB: return 0x3<<11; - case AMOVW: return 0x0<<11; - case ACMP: return 0x1<<11; - } - diag("bad thumbop opri %d", a); - prasm(curp); - return 0; -} - -static int32 -thumbophh(int a) -{ - switch(a) { - case AADD: return 0x0<<8; - case AMOVW: return 0x2<<8; - case ACMP: return 0x1<<8; - } - diag("bad thumbop ophh %d", a); - prasm(curp); - return 0; -} - -static int32 -thumbopbra(int a) -{ - switch(a) { - case ABEQ: return 0x0<<8; - case ABNE: return 0x1<<8; - case ABCS: return 0x2<<8; - case ABHS: return 0x2<<8; - case ABCC: return 0x3<<8; - case ABLO: return 0x3<<8; - case ABMI: return 0x4<<8; - case ABPL: return 0x5<<8; - case ABVS: return 0x6<<8; - case ABVC: return 0x7<<8; - case ABHI: return 0x8<<8; - case ABLS: return 0x9<<8; - case ABGE: return 0xa<<8; - case ABLT: return 0xb<<8; - case ABGT: return 0xc<<8; - case ABLE: return 0xd<<8; - } - diag("bad thumbop opbra %d", a); - prasm(curp); - return 0; -} - -static int32 -thumbopmv(int a, int ld) -{ - switch(a) { - case AMOVW: return (ld ? 0xd : 0xc)<<11; - case AMOVH: - case AMOVHU: return (ld ? 0x11: 0x10)<<11; - case AMOVB: - case AMOVBU: return (ld ? 0xf : 0xe)<<11; - } - diag("bad thumbop opmv %d", a); - prasm(curp); - return 0; -} - -static void -lowreg(Prog *p, int r) -{ - if(high(r)) - diag("high reg [%P]", p); -} - -static void -mult(Prog *p, int n, int m) -{ - if(m*(n/m) != n) - diag("%d not M(%d) [%P]", n, m, p); -} - -static void -numr(Prog *p, int n, int min, int max) -{ - if(n < min || n > max) - diag("%d not in %d-%d [%P]", n, min, max, p); -} - -static void -regis(Prog *p, int r, int r1, int r2) -{ - if(r != r1 && r != r2) - diag("reg %d not %d or %d [%P]", r, r1, r2, p); -} - -void -hputl(int n) -{ - cbp[1] = n>>8; - cbp[0] = n; - cbp += 2; - cbc -= 2; - if(cbc <= 0) - cflush(); -} - -void -thumbcount() -{ - int i, c = 0, t = 0; - - for (i = 0; i < OPCNTSZ; i++) - t += opcount[i]; - if(t == 0) - return; - for (i = 0; i < OPCNTSZ; i++){ - c += opcount[i]; - print("%d: %d %d %d%%\n", i, opcount[i], c, (opcount[i]*100+t/2)/t); - } -} - -char *op1[] = { "lsl", "lsr", "asr" }; -char *op2[] = { "add", "sub" }; -char *op3[] = { "movw", "cmp", "add", "sub" }; -char *op4[] = { "and", "eor", "lsl", "lsr", "asr", "adc", "sbc", "ror", - "tst", "neg", "cmp", "cmpn", "or", "mul", "bitc", "movn" }; -char *op5[] = { "add", "cmp", "movw", "bx" }; -char *op6[] = { "smovw", "smovh", "smovb", "lmovb", "lmovw", "lmovhu", "lmovbu", "lmovh" }; -char *op7[] = { "smovw", "lmovw", "smovb", "lmovbu" }; -char *op8[] = { "smovh", "lmovhu" }; -char *op9[] = { "smovw", "lmovw" }; -char *op10[] = { "push", "pop" }; -char *op11[] = { "stmia", "ldmia" }; - -char *cond[] = { "eq", "ne", "hs", "lo", "mi", "pl", "vs", "vc", - "hi", "ls", "ge", "lt", "gt", "le", "al", "nv" }; - -#define B(h, l) bits(i, h, l) -#define IMM(h, l) B(h, l) -#define REG(h, l) reg(B(h, l)) -#define LHREG(h, l, lh) lhreg(B(h, l), B(lh, lh)) -#define COND(h, l) cond[B(h, l)] -#define OP1(h, l) op1[B(h, l)] -#define OP2(h, l) op2[B(h, l)] -#define OP3(h, l) op3[B(h, l)] -#define OP4(h, l) op4[B(h, l)] -#define OP5(h, l) op5[B(h, l)] -#define OP6(h, l) op6[B(h, l)] -#define OP7(h, l) op7[B(h, l)] -#define OP8(h, l) op8[B(h, l)] -#define OP9(h, l) op9[B(h, l)] -#define OP10(h, l) op10[B(h, l)] -#define OP11(h, l) op11[B(h, l)] -#define SBZ(h, l) if(IMM(h, l) != 0) diag("%x: %x bits %d,%d not zero", pc, i, h, l) -#define SNBZ(h, l) if(IMM(h, l) == 0) diag("%x: %x bits %d,%d zero", pc, i, h, l) -#define SBO(h, l) if(IMM(h, l) != 1) diag("%x: %x bits %d,%d not one", pc, i, h, l) - -static int -bits(int i, int h, int l) -{ - if(h < l) - diag("h < l in bits"); - return (i&(((1<<(h-l+1))-1)<<l))>>l; -} - -static char * -reg(int r) -{ - static char s[4][4]; - static int i = 0; - - if(r < 0 || r > 7) - diag("register %d out of range", r); - i++; - if(i == 4) - i = 0; - sprint(s[i], "r%d", r); - return s[i]; -} - -static char *regnames[] = { "sp", "lr", "pc" }; - -static char * -lhreg(int r, int lh) -{ - static char s[4][4]; - static int i = 0; - - if(lh == 0) - return reg(r); - if(r < 0 || r > 7) - diag("high register %d out of range", r); - i++; - if(i == 4) - i = 0; - if(r >= 5) - sprint(s[i], "%s", regnames[r-5]); - else - sprint(s[i], "r%d", r+8); - return s[i]; -} - -static void -illegal(int i, int pc) -{ - diag("%x: %x illegal instruction", pc, i); -} - -static void -dis(int i, int pc) -{ - static int lasto; - int o, l; - char *op; - - print("%x: %x: ", pc, i); - if(i&0xffff0000) - illegal(i, pc); - o = B(15, 13); - switch(o){ - case 0: - o = B(12, 11); - switch(o){ - case 0: - case 1: - case 2: - print("%s %d, %s, %s\n", OP1(12, 11), IMM(10, 6), REG(5, 3), REG(2, 0)); - return; - case 3: - if(B(10, 10) == 0) - print("%s %s, %s, %s\n", OP2(9, 9), REG(8, 6), REG(5, 3), REG(2, 0)); - else - print("%s %d, %s, %s\n", OP2(9, 9), IMM(8, 6), REG(5, 3), REG(2, 0)); - return; - } - case 1: - print("%s %d, %s\n", OP3(12, 11), IMM(7, 0), REG(10, 8)); - return; - case 2: - o = B(12, 10); - if(o == 0){ - print("%s %s, %s\n", OP4(9, 6), REG(5, 3), REG(2, 0)); - return; - } - if(o == 1){ - o = B(9, 8); - if(o == 3){ - SBZ(7, 7); - SBZ(2, 0); - print("%s %s\n", OP5(9, 8), LHREG(5, 3, 6)); - return; - } - SNBZ(7, 6); - print("%s %s, %s\n", OP5(9, 8), LHREG(5, 3, 6), LHREG(2, 0, 7)); - return; - } - if(o == 2 || o == 3){ - print("movw %d(pc)[%x], %s\n", 4*IMM(7, 0), 4*IMM(7, 0)+pc+4, REG(10, 8)); - return; - } - op = OP6(11, 9); - if(*op == 'l') - print("%s [%s, %s], %s\n", op+1, REG(8, 6), REG(5, 3), REG(2, 0)); - else - print("%s %s, [%s, %s]\n", op+1, REG(2, 0), REG(8, 6), REG(5, 3)); - return; - case 3: - op = OP7(12, 11); - if(B(12, 11) == 0 || B(12,11) == 1) - l = 4; - else - l = 1; - if(*op == 'l') - print("%s %d(%s), %s\n", op+1, l*IMM(10, 6), REG(5, 3), REG(2, 0)); - else - print("%s %s, %d(%s)\n", op+1, REG(2, 0), l*IMM(10, 6), REG(5, 3)); - return; - case 4: - if(B(12, 12) == 0){ - op = OP8(11, 11); - if(*op == 'l') - print("%s %d(%s), %s\n", op+1, 2*IMM(10, 6), REG(5, 3), REG(2, 0)); - else - print("%s %s, %d(%s)\n", op+1, REG(2, 0), 2*IMM(10, 6), REG(5, 3)); - return; - } - op = OP9(11, 11); - if(*op == 'l') - print("%s %d(sp), %s\n", op+1, 4*IMM(7, 0), REG(10, 8)); - else - print("%s %s, %d(sp)\n", op+1, REG(10, 8), 4*IMM(7, 0)); - return; - case 5: - if(B(12, 12) == 0){ - if(B(11, 11) == 0) - print("add %d, pc, %s\n", 4*IMM(7, 0), REG(10, 8)); - else - print("add %d, sp, %s\n", 4*IMM(7, 0), REG(10, 8)); - return; - } - if(B(11, 8) == 0){ - print("%s %d, sp\n", OP2(7, 7), 4*IMM(6, 0)); - return; - } - SBO(10, 10); - SBZ(9, 9); - if(B(8, 8) == 0) - print("%s sp, %d\n", OP10(11, 11), IMM(7, 0)); - else - print("%s sp, %d|15\n", OP10(11, 11), IMM(7, 0)); - return; - case 6: - if(B(12, 12) == 0){ - print("%s %s, %d\n", OP11(11, 11), REG(10, 8), IMM(7, 0)); - return; - } - if(B(11, 8) == 0xf){ - print("swi %d\n", IMM(7, 0)); - return; - } - o = IMM(7, 0); - if(o&0x80) - o |= 0xffffff00; - o = pc+4+(o<<1); - print("b%s %x\n", COND(11, 8), o); - return; - case 7: - o = B(12, 11); - switch(o){ - case 0: - o = IMM(10, 0); - if(o&0x400) - o |= 0xfffff800; - o = pc+4+(o<<1); - print("b %x\n", o); - return; - case 1: - illegal(i, pc); - return; - case 2: - lasto = IMM(10, 0); - print("bl\n"); - return; - case 3: - if(lasto&0x400) - lasto |= 0xfffff800; - o = IMM(10, 0); - o = (pc-2)+4+(o<<1)+(lasto<<12); - print("bl %x\n", o); - return; - } - } -} diff --git a/src/cmd/6g/gg.h b/src/cmd/6g/gg.h index 7efb2c252..2493771a0 100644 --- a/src/cmd/6g/gg.h +++ b/src/cmd/6g/gg.h @@ -23,6 +23,7 @@ struct Addr Sym* gotype; Sym* sym; + Node* node; int width; uchar type; uchar index; diff --git a/src/cmd/6g/gsubr.c b/src/cmd/6g/gsubr.c index ed98d1bc9..ae6ae5765 100644 --- a/src/cmd/6g/gsubr.c +++ b/src/cmd/6g/gsubr.c @@ -988,7 +988,7 @@ naddr(Node *n, Addr *a, int canemitcode) a->index = D_NONE; a->type = D_NONE; a->gotype = S; - + a->node = N; if(n == N) return; @@ -1067,6 +1067,8 @@ naddr(Node *n, Addr *a, int canemitcode) break; case PAUTO: a->type = D_AUTO; + if (n->sym) + a->node = n->orig; break; case PPARAM: case PPARAMOUT: diff --git a/src/cmd/6g/reg.c b/src/cmd/6g/reg.c index b4b5b7d6b..af9b29cbc 100644 --- a/src/cmd/6g/reg.c +++ b/src/cmd/6g/reg.c @@ -33,6 +33,8 @@ #define EXTERN #include "opt.h" +#define NREGVAR 32 /* 16 general + 16 floating */ +#define REGBITS ((uint32)0xffffffff) #define P2R(p) (Reg*)(p->reg) static int first = 1; @@ -114,6 +116,41 @@ setaddrs(Bits bit) } } +static char* regname[] = { + ".AX", + ".CX", + ".DX", + ".BX", + ".SP", + ".BP", + ".SI", + ".DI", + ".R8", + ".R9", + ".R10", + ".R11", + ".R12", + ".R13", + ".R14", + ".R15", + ".X0", + ".X1", + ".X2", + ".X3", + ".X4", + ".X5", + ".X6", + ".X7", + ".X8", + ".X9", + ".X10", + ".X11", + ".X12", + ".X13", + ".X14", + ".X15", +}; + void regopt(Prog *firstp) { @@ -143,6 +180,17 @@ regopt(Prog *firstp) firstr = R; lastr = R; nvar = 0; + + /* + * control flow is more complicated in generated go code + * than in generated c code. define pseudo-variables for + * registers, so we have complete register usage information. + */ + nvar = NREGVAR; + memset(var, 0, NREGVAR*sizeof var[0]); + for(i=0; i<NREGVAR; i++) + var[i].sym = lookup(regname[i]); + regbits = RtoB(D_SP); for(z=0; z<BITS; z++) { externs.b[z] = 0; @@ -247,6 +295,9 @@ regopt(Prog *firstp) case ACOMISD: case AUCOMISS: case AUCOMISD: + case ATESTB: + case ATESTL: + case ATESTQ: for(z=0; z<BITS; z++) r->use2.b[z] |= bit.b[z]; break; @@ -254,6 +305,7 @@ regopt(Prog *firstp) /* * right side write */ + case ALEAQ: case ANOP: case AMOVL: case AMOVQ: @@ -261,6 +313,8 @@ regopt(Prog *firstp) case AMOVW: case AMOVBLSX: case AMOVBLZX: + case AMOVBWSX: + case AMOVBWZX: case AMOVBQSX: case AMOVBQZX: case AMOVLQSX: @@ -269,6 +323,7 @@ regopt(Prog *firstp) case AMOVWLZX: case AMOVWQSX: case AMOVWQZX: + case APOPQ: case AMOVSS: case AMOVSD: @@ -357,6 +412,8 @@ regopt(Prog *firstp) case AIMULL: case AIMULQ: case AIMULW: + case ANEGB: + case ANEGW: case ANEGL: case ANEGQ: case ANOTL: @@ -366,6 +423,23 @@ regopt(Prog *firstp) case ASBBL: case ASBBQ: + case ASETCC: + case ASETCS: + case ASETEQ: + case ASETGE: + case ASETGT: + case ASETHI: + case ASETLE: + case ASETLS: + case ASETLT: + case ASETMI: + case ASETNE: + case ASETOC: + case ASETOS: + case ASETPC: + case ASETPL: + case ASETPS: + case AXCHGB: case AXCHGW: case AXCHGL: @@ -411,32 +485,44 @@ regopt(Prog *firstp) if(p->to.type != D_NONE) break; - case AIDIVB: case AIDIVL: - case AIDIVQ: case AIDIVW: - case AIMULB: - case ADIVB: + case AIDIVQ: case ADIVL: - case ADIVQ: case ADIVW: - case AMULB: + case ADIVQ: case AMULL: - case AMULQ: case AMULW: + case AMULQ: + r->set.b[0] |= RtoB(D_AX) | RtoB(D_DX); + r->use1.b[0] |= RtoB(D_AX) | RtoB(D_DX); + break; + + case AIDIVB: + case AIMULB: + case ADIVB: + case AMULB: + r->set.b[0] |= RtoB(D_AX); + r->use1.b[0] |= RtoB(D_AX); + break; case ACWD: - case ACDQ: - case ACQO: - r->regu |= RtoB(D_AX) | RtoB(D_DX); + r->set.b[0] |= RtoB(D_AX) | RtoB(D_DX); + r->use1.b[0] |= RtoB(D_AX); break; + case ACDQ: + r->set.b[0] |= RtoB(D_DX); + r->use1.b[0] |= RtoB(D_AX); + break; + case AREP: case AREPN: case ALOOP: case ALOOPEQ: case ALOOPNE: - r->regu |= RtoB(D_CX); + r->set.b[0] |= RtoB(D_CX); + r->use1.b[0] |= RtoB(D_CX); break; case AMOVSB: @@ -447,7 +533,8 @@ regopt(Prog *firstp) case ACMPSL: case ACMPSQ: case ACMPSW: - r->regu |= RtoB(D_SI) | RtoB(D_DI); + r->set.b[0] |= RtoB(D_SI) | RtoB(D_DI); + r->use1.b[0] |= RtoB(D_SI) | RtoB(D_DI); break; case ASTOSB: @@ -458,16 +545,22 @@ regopt(Prog *firstp) case ASCASL: case ASCASQ: case ASCASW: - r->regu |= RtoB(D_AX) | RtoB(D_DI); + r->set.b[0] |= RtoB(D_DI); + r->use1.b[0] |= RtoB(D_AX) | RtoB(D_DI); break; case AINSB: case AINSL: case AINSW: + r->set.b[0] |= RtoB(D_DX) | RtoB(D_DI); + r->use1.b[0] |= RtoB(D_DI); + break; + case AOUTSB: case AOUTSL: case AOUTSW: - r->regu |= RtoB(D_DI) | RtoB(D_DX); + r->set.b[0] |= RtoB(D_DI); + r->use1.b[0] |= RtoB(D_DX) | RtoB(D_DI); break; } } @@ -574,6 +667,24 @@ loop2: dumpit("pass4", firstr); /* + * pass 4.5 + * move register pseudo-variables into regu. + */ + for(r = firstr; r != R; r = r->link) { + r->regu = (r->refbehind.b[0] | r->set.b[0]) & REGBITS; + + r->set.b[0] &= ~REGBITS; + r->use1.b[0] &= ~REGBITS; + r->use2.b[0] &= ~REGBITS; + r->refbehind.b[0] &= ~REGBITS; + r->refahead.b[0] &= ~REGBITS; + r->calbehind.b[0] &= ~REGBITS; + r->calahead.b[0] &= ~REGBITS; + r->regdiff.b[0] &= ~REGBITS; + r->act.b[0] &= ~REGBITS; + } + + /* * pass 5 * isolate regions * calculate costs (paint1) @@ -726,6 +837,7 @@ addmove(Reg *r, int bn, int rn, int f) a->etype = v->etype; a->type = v->name; a->gotype = v->gotype; + a->node = v->node; // need to clean this up with wptr and // some of the defaults @@ -818,6 +930,7 @@ mkvar(Reg *r, Adr *a) { Var *v; int i, t, n, et, z, w, flag; + uint32 regu; int32 o; Bits bit; Sym *s; @@ -829,14 +942,17 @@ mkvar(Reg *r, Adr *a) if(t == D_NONE) goto none; - if(r != R) { - r->regu |= doregbits(t); - r->regu |= doregbits(a->index); - } + if(r != R) + r->use1.b[0] |= doregbits(a->index); switch(t) { default: - goto none; + regu = doregbits(t); + if(regu == 0) + goto none; + bit = zbits; + bit.b[0] = regu; + return bit; case D_ADDR: a->type = a->index; @@ -906,6 +1022,7 @@ mkvar(Reg *r, Adr *a) v->etype = et; v->width = w; v->addr = flag; // funny punning + v->node = a->node; if(debug['R']) print("bit=%2d et=%2d w=%d %S %D\n", i, et, w, s, a); diff --git a/src/cmd/6l/asm.c b/src/cmd/6l/asm.c index 320f4c9e9..4c04112b7 100644 --- a/src/cmd/6l/asm.c +++ b/src/cmd/6l/asm.c @@ -698,7 +698,7 @@ asmb(void) { int32 magic; int a, dynsym; - vlong vl, startva, symo, elfsymo, elfstro, elfsymsize, machlink; + vlong vl, startva, symo, machlink; ElfEhdr *eh; ElfPhdr *ph, *pph; ElfShdr *sh; @@ -709,9 +709,6 @@ asmb(void) Bflush(&bso); elftextsh = 0; - elfsymsize = 0; - elfstro = 0; - elfsymo = 0; if(debug['v']) Bprint(&bso, "%5.2f codeblk\n", cputime()); @@ -790,36 +787,13 @@ asmb(void) symo = rnd(symo, PEFILEALIGN); break; } + seek(cout, symo, 0); switch(HEADTYPE) { default: if(iself) { - /* - * the symbol information is stored as - * 32-bit symbol table size - * 32-bit line number table size - * symbol table - * line number table - */ - seek(cout, symo+8, 0); - if(debug['v']) - Bprint(&bso, "%5.2f sp\n", cputime()); - Bflush(&bso); - if(debug['v']) - Bprint(&bso, "%5.2f pc\n", cputime()); - Bflush(&bso); - if(!debug['s']) - strnput("", INITRND-(8+symsize+lcsize)%INITRND); - cflush(); seek(cout, symo, 0); - lputl(symsize); - lputl(lcsize); + asmelfsym(); cflush(); - elfsymo = symo+8+symsize+lcsize; - seek(cout, elfsymo, 0); - asmelfsym64(); - cflush(); - elfstro = seek(cout, 0, 1); - elfsymsize = elfstro - elfsymo; ewrite(cout, elfstrdat, elfstrsize); if(debug['v']) @@ -830,7 +804,6 @@ asmb(void) break; case Hdarwin: case Hwindows: - seek(cout, symo, 0); if(debug['v']) Bprint(&bso, "%5.2f dwarf\n", cputime()); @@ -1054,15 +1027,15 @@ asmb(void) sh = newElfShdr(elfstr[ElfStrSymtab]); sh->type = SHT_SYMTAB; - sh->off = elfsymo; - sh->size = elfsymsize; + sh->off = symo; + sh->size = symsize; sh->addralign = 8; sh->entsize = 24; sh->link = eh->shnum; // link to strtab sh = newElfShdr(elfstr[ElfStrStrtab]); sh->type = SHT_STRTAB; - sh->off = elfstro; + sh->off = symo+symsize; sh->size = elfstrsize; sh->addralign = 1; @@ -1149,6 +1122,10 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) Auto *a; Sym *s; + s = lookup("etext", 0); + if(s->type == STEXT) + put(s, s->name, 'T', s->value, s->size, s->version, 0); + for(s=allsym; s!=S; s=s->allsym) { if(s->hide) continue; diff --git a/src/cmd/8g/gg.h b/src/cmd/8g/gg.h index 57cd1b56b..7da60d767 100644 --- a/src/cmd/8g/gg.h +++ b/src/cmd/8g/gg.h @@ -25,6 +25,7 @@ struct Addr Sym* gotype; Sym* sym; + Node* node; int width; uchar type; uchar index; diff --git a/src/cmd/8g/gsubr.c b/src/cmd/8g/gsubr.c index 5ad35fdce..6bcc3eed8 100644 --- a/src/cmd/8g/gsubr.c +++ b/src/cmd/8g/gsubr.c @@ -1720,6 +1720,7 @@ naddr(Node *n, Addr *a, int canemitcode) a->index = D_NONE; a->type = D_NONE; a->gotype = S; + a->node = N; if(n == N) return; @@ -1777,6 +1778,8 @@ naddr(Node *n, Addr *a, int canemitcode) break; case PAUTO: a->type = D_AUTO; + if (n->sym) + a->node = n->orig; break; case PPARAM: case PPARAMOUT: diff --git a/src/cmd/8g/reg.c b/src/cmd/8g/reg.c index 1465d372c..a2f3def37 100644 --- a/src/cmd/8g/reg.c +++ b/src/cmd/8g/reg.c @@ -33,6 +33,8 @@ #define EXTERN #include "opt.h" +#define NREGVAR 8 +#define REGBITS ((uint32)0xff) #define P2R(p) (Reg*)(p->reg) static int first = 1; @@ -114,6 +116,8 @@ setaddrs(Bits bit) } } +static char* regname[] = { ".ax", ".cx", ".dx", ".bx", ".sp", ".bp", ".si", ".di" }; + void regopt(Prog *firstp) { @@ -142,7 +146,17 @@ regopt(Prog *firstp) r1 = R; firstr = R; lastr = R; - nvar = 0; + + /* + * control flow is more complicated in generated go code + * than in generated c code. define pseudo-variables for + * registers, so we have complete register usage information. + */ + nvar = NREGVAR; + memset(var, 0, NREGVAR*sizeof var[0]); + for(i=0; i<NREGVAR; i++) + var[i].sym = lookup(regname[i]); + regbits = RtoB(D_SP); for(z=0; z<BITS; z++) { externs.b[z] = 0; @@ -249,14 +263,19 @@ regopt(Prog *firstp) /* * right side write */ + case AFSTSW: + case ALEAL: case ANOP: case AMOVL: case AMOVB: case AMOVW: case AMOVBLSX: case AMOVBLZX: + case AMOVBWSX: + case AMOVBWZX: case AMOVWLSX: case AMOVWLZX: + case APOPL: for(z=0; z<BITS; z++) r->set.b[z] |= bit.b[z]; break; @@ -321,6 +340,23 @@ regopt(Prog *firstp) case AADCL: case ASBBL: + case ASETCC: + case ASETCS: + case ASETEQ: + case ASETGE: + case ASETGT: + case ASETHI: + case ASETLE: + case ASETLS: + case ASETLT: + case ASETMI: + case ASETNE: + case ASETOC: + case ASETOS: + case ASETPC: + case ASETPL: + case ASETPS: + case AXCHGB: case AXCHGW: case AXCHGL: @@ -349,20 +385,32 @@ regopt(Prog *firstp) if(p->to.type != D_NONE) break; - case AIDIVB: case AIDIVL: case AIDIVW: - case AIMULB: - case ADIVB: case ADIVL: case ADIVW: - case AMULB: case AMULL: case AMULW: + r->set.b[0] |= RtoB(D_AX) | RtoB(D_DX); + r->use1.b[0] |= RtoB(D_AX) | RtoB(D_DX); + break; + + case AIDIVB: + case AIMULB: + case ADIVB: + case AMULB: + r->set.b[0] |= RtoB(D_AX); + r->use1.b[0] |= RtoB(D_AX); + break; case ACWD: + r->set.b[0] |= RtoB(D_AX) | RtoB(D_DX); + r->use1.b[0] |= RtoB(D_AX); + break; + case ACDQ: - r->regu |= RtoB(D_AX) | RtoB(D_DX); + r->set.b[0] |= RtoB(D_DX); + r->use1.b[0] |= RtoB(D_AX); break; case AREP: @@ -370,7 +418,8 @@ regopt(Prog *firstp) case ALOOP: case ALOOPEQ: case ALOOPNE: - r->regu |= RtoB(D_CX); + r->set.b[0] |= RtoB(D_CX); + r->use1.b[0] |= RtoB(D_CX); break; case AMOVSB: @@ -379,7 +428,8 @@ regopt(Prog *firstp) case ACMPSB: case ACMPSL: case ACMPSW: - r->regu |= RtoB(D_SI) | RtoB(D_DI); + r->set.b[0] |= RtoB(D_SI) | RtoB(D_DI); + r->use1.b[0] |= RtoB(D_SI) | RtoB(D_DI); break; case ASTOSB: @@ -388,16 +438,22 @@ regopt(Prog *firstp) case ASCASB: case ASCASL: case ASCASW: - r->regu |= RtoB(D_AX) | RtoB(D_DI); + r->set.b[0] |= RtoB(D_DI); + r->use1.b[0] |= RtoB(D_AX) | RtoB(D_DI); break; case AINSB: case AINSL: case AINSW: + r->set.b[0] |= RtoB(D_DX) | RtoB(D_DI); + r->use1.b[0] |= RtoB(D_DI); + break; + case AOUTSB: case AOUTSL: case AOUTSW: - r->regu |= RtoB(D_DI) | RtoB(D_DX); + r->set.b[0] |= RtoB(D_DI); + r->use1.b[0] |= RtoB(D_DX) | RtoB(D_DI); break; } } @@ -504,6 +560,24 @@ loop2: dumpit("pass4", firstr); /* + * pass 4.5 + * move register pseudo-variables into regu. + */ + for(r = firstr; r != R; r = r->link) { + r->regu = (r->refbehind.b[0] | r->set.b[0]) & REGBITS; + + r->set.b[0] &= ~REGBITS; + r->use1.b[0] &= ~REGBITS; + r->use2.b[0] &= ~REGBITS; + r->refbehind.b[0] &= ~REGBITS; + r->refahead.b[0] &= ~REGBITS; + r->calbehind.b[0] &= ~REGBITS; + r->calahead.b[0] &= ~REGBITS; + r->regdiff.b[0] &= ~REGBITS; + r->act.b[0] &= ~REGBITS; + } + + /* * pass 5 * isolate regions * calculate costs (paint1) @@ -656,6 +730,7 @@ addmove(Reg *r, int bn, int rn, int f) a->etype = v->etype; a->type = v->name; a->gotype = v->gotype; + a->node = v->node; // need to clean this up with wptr and // some of the defaults @@ -732,7 +807,7 @@ Bits mkvar(Reg *r, Adr *a) { Var *v; - int i, t, n, et, z, w, flag; + int i, t, n, et, z, w, flag, regu; int32 o; Bits bit; Sym *s; @@ -744,14 +819,17 @@ mkvar(Reg *r, Adr *a) if(t == D_NONE) goto none; - if(r != R) { - r->regu |= doregbits(t); - r->regu |= doregbits(a->index); - } + if(r != R) + r->use1.b[0] |= doregbits(a->index); switch(t) { default: - goto none; + regu = doregbits(t); + if(regu == 0) + goto none; + bit = zbits; + bit.b[0] = regu; + return bit; case D_ADDR: a->type = a->index; @@ -821,6 +899,7 @@ mkvar(Reg *r, Adr *a) v->etype = et; v->width = w; v->addr = flag; // funny punning + v->node = a->node; if(debug['R']) print("bit=%2d et=%2d w=%d %S %D flag=%d\n", i, et, w, s, a, v->addr); diff --git a/src/cmd/8l/asm.c b/src/cmd/8l/asm.c index c426a88a4..cb900d28d 100644 --- a/src/cmd/8l/asm.c +++ b/src/cmd/8l/asm.c @@ -663,7 +663,7 @@ asmb(void) { int32 v, magic; int a, dynsym; - uint32 symo, startva, machlink, elfsymo, elfstro, elfsymsize; + uint32 symo, startva, machlink; ElfEhdr *eh; ElfPhdr *ph, *pph; ElfShdr *sh; @@ -675,10 +675,6 @@ asmb(void) Bprint(&bso, "%5.2f asmb\n", cputime()); Bflush(&bso); - elfsymsize = 0; - elfstro = 0; - elfsymo = 0; - sect = segtext.sect; seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0); codeblk(sect->vaddr, sect->len); @@ -724,10 +720,10 @@ asmb(void) if(iself) goto Elfsym; case Hgarbunix: - seek(cout, rnd(HEADR+segtext.filelen, 8192)+segdata.filelen, 0); + symo = rnd(HEADR+segtext.filelen, 8192)+segdata.filelen; break; case Hunixcoff: - seek(cout, rnd(HEADR+segtext.filelen, INITRND)+segdata.filelen, 0); + symo = rnd(HEADR+segtext.filelen, INITRND)+segdata.filelen; break; case Hplan9x32: symo = HEADR+segtext.filelen+segdata.filelen; @@ -749,17 +745,14 @@ asmb(void) symo = rnd(symo, PEFILEALIGN); break; } + seek(cout, symo, 0); switch(HEADTYPE) { default: if(iself) { if(debug['v']) Bprint(&bso, "%5.2f elfsym\n", cputime()); - elfsymo = symo+8+symsize+lcsize; - seek(cout, elfsymo, 0); - asmelfsym32(); + asmelfsym(); cflush(); - elfstro = seek(cout, 0, 1); - elfsymsize = elfstro - elfsymo; ewrite(cout, elfstrdat, elfstrsize); if(debug['v']) @@ -768,10 +761,9 @@ asmb(void) } break; case Hplan9x32: - seek(cout, symo, 0); asmplan9sym(); cflush(); - + sym = lookup("pclntab", 0); if(sym != nil) { lcsize = sym->np; @@ -783,7 +775,6 @@ asmb(void) break; case Hdarwin: case Hwindows: - seek(cout, symo, 0); if(debug['v']) Bprint(&bso, "%5.2f dwarf\n", cputime()); dwarfemitdebugsections(); @@ -1110,15 +1101,15 @@ asmb(void) sh = newElfShdr(elfstr[ElfStrSymtab]); sh->type = SHT_SYMTAB; - sh->off = elfsymo; - sh->size = elfsymsize; + sh->off = symo; + sh->size = symsize; sh->addralign = 4; sh->entsize = 16; sh->link = eh->shnum; // link to strtab sh = newElfShdr(elfstr[ElfStrStrtab]); sh->type = SHT_STRTAB; - sh->off = elfstro; + sh->off = symo+symsize; sh->size = elfstrsize; sh->addralign = 1; diff --git a/src/cmd/8l/l.h b/src/cmd/8l/l.h index 8f39ef519..7e7cd5d63 100644 --- a/src/cmd/8l/l.h +++ b/src/cmd/8l/l.h @@ -260,11 +260,15 @@ EXTERN union #define cbuf u.obuf #define xbuf u.ibuf -#pragma varargck type "A" uint +#pragma varargck type "A" int #pragma varargck type "D" Adr* +#pragma varargck type "I" int +#pragma varargck type "I" uchar* #pragma varargck type "P" Prog* #pragma varargck type "R" int #pragma varargck type "S" char* +#pragma varargck type "Y" Sym* +#pragma varargck type "i" char* EXTERN int32 HEADR; EXTERN int32 HEADTYPE; @@ -383,11 +387,6 @@ void deadcode(void); #define WPUT(a) wputl(a) #define VPUT(a) vputl(a) -#pragma varargck type "D" Adr* -#pragma varargck type "P" Prog* -#pragma varargck type "R" int -#pragma varargck type "A" int - /* Used by ../ld/dwarf.c */ enum { diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index fa7602cf2..10411e94f 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -660,11 +660,14 @@ func (p *Package) gccName() (ret string) { } // gccMachine returns the gcc -m flag to use, either "-m32" or "-m64". -func (p *Package) gccMachine() string { - if p.PtrSize == 8 { - return "-m64" - } - return "-m32" +func (p *Package) gccMachine() []string { + switch runtime.GOARCH { + case "amd64": + return []string{"-m64"} + case "386": + return []string{"-m32"} + } + return nil } const gccTmp = "_obj/_cgo_.o" @@ -674,7 +677,6 @@ const gccTmp = "_obj/_cgo_.o" func (p *Package) gccCmd() []string { c := []string{ p.gccName(), - p.gccMachine(), "-Wall", // many warnings "-Werror", // warnings are errors "-o" + gccTmp, // write object to tmp @@ -684,6 +686,7 @@ func (p *Package) gccCmd() []string { "-xc", // input language is C } c = append(c, p.GccOptions...) + c = append(c, p.gccMachine()...) c = append(c, "-") //read input from standard input return c } @@ -719,7 +722,8 @@ func (p *Package) gccDebug(stdin []byte) *dwarf.Data { // #defines that gcc encountered while processing the input // and its included files. func (p *Package) gccDefines(stdin []byte) string { - base := []string{p.gccName(), p.gccMachine(), "-E", "-dM", "-xc"} + base := []string{p.gccName(), "-E", "-dM", "-xc"} + base = append(base, p.gccMachine()...) stdout, _ := runGcc(stdin, append(append(base, p.GccOptions...), "-")) return stdout } diff --git a/src/cmd/gc/dcl.c b/src/cmd/gc/dcl.c index 78c676346..83be82f92 100644 --- a/src/cmd/gc/dcl.c +++ b/src/cmd/gc/dcl.c @@ -438,20 +438,6 @@ newtype(Sym *s) return t; } -/* - * type check top level declarations - */ -void -dclchecks(void) -{ - NodeList *l; - - for(l=externdcl; l; l=l->next) { - if(l->n->op != ONAME) - continue; - typecheck(&l->n, Erv); - } -} /* * := declarations diff --git a/src/cmd/gc/gen.c b/src/cmd/gc/gen.c index 0b6f5bbd8..feb55e905 100644 --- a/src/cmd/gc/gen.c +++ b/src/cmd/gc/gen.c @@ -51,6 +51,8 @@ allocparams(void) } if(n->op != ONAME || n->class != PAUTO) continue; + if (n->xoffset != BADWIDTH) + continue; if(n->type == T) continue; dowidth(n->type); @@ -669,14 +671,18 @@ dotoffset(Node *n, int *oary, Node **nn) * make a new off the books */ void -tempname(Node *n, Type *t) +tempname(Node *nn, Type *t) { + Node *n; Sym *s; uint32 w; if(stksize < 0) fatal("tempname not during code generation"); + if (curfn == N) + fatal("no curfn for tempname"); + if(t == T) { yyerror("tempname called with nil type"); t = types[TINT32]; @@ -687,14 +693,15 @@ tempname(Node *n, Type *t) snprint(namebuf, sizeof(namebuf), "autotmp_%.4d", statuniqgen); statuniqgen++; s = lookup(namebuf); - memset(n, 0, sizeof(*n)); - n->op = ONAME; + n = nod(ONAME, N, N); n->sym = s; n->type = t; n->class = PAUTO; n->addable = 1; n->ullman = 1; n->noescape = 1; + n->curfn = curfn; + curfn->dcl = list(curfn->dcl, n); dowidth(t); w = t->width; @@ -703,5 +710,8 @@ tempname(Node *n, Type *t) if(thechar == '5') stksize = rnd(stksize, widthptr); n->xoffset = -stksize; - n->pun = anyregalloc(); + + // print("\ttmpname (%d): %N\n", stksize, n); + + *nn = *n; } diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h index f5c0443f8..86db48391 100644 --- a/src/cmd/gc/go.h +++ b/src/cmd/gc/go.h @@ -225,7 +225,7 @@ struct Node Type* realtype; // as determined by typecheck NodeList* list; NodeList* rlist; - Node* orig; // original form, for printing + Node* orig; // original form, for printing, and tracking copies of ONAMEs // for-body NodeList* ninit; @@ -273,6 +273,7 @@ struct Node int32 lineno; int32 endlineno; vlong xoffset; + int32 stkdelta; // offset added by stack frame compaction phase. int32 ostk; int32 iota; }; @@ -547,6 +548,7 @@ struct Var vlong offset; Sym* sym; Sym* gotype; + Node* node; int width; char name; char etype; @@ -862,7 +864,6 @@ NodeList* checkarglist(NodeList *all, int input); Node* colas(NodeList *left, NodeList *right); void colasdefn(NodeList *left, Node *defn); NodeList* constiter(NodeList *vl, Node *t, NodeList *cl); -void dclchecks(void); Node* dclname(Sym *s); void declare(Node *n, int ctxt); Type* dostruct(NodeList *l, int et); @@ -1108,6 +1109,7 @@ int istype(Type *t, int et); void linehist(char *file, int32 off, int relative); NodeList* list(NodeList *l, Node *n); NodeList* list1(Node *n); +void listsort(NodeList**, int(*f)(Node*, Node*)); Node* liststmt(NodeList *l); NodeList* listtreecopy(NodeList *l); Sym* lookup(char *name); @@ -1166,6 +1168,11 @@ int exportassignok(Type *t, char *desc); int islvalue(Node *n); Node* typecheck(Node **np, int top); void typechecklist(NodeList *l, int top); +Node* typecheckdef(Node *n); +void resumetypecopy(void); +void copytype(Node *n, Type *t); +void defertypecopy(Node *n, Type *t); +void queuemethod(Node *n); /* * unsafe.c @@ -1177,15 +1184,10 @@ Node* unsafenmagic(Node *n); */ Node* callnew(Type *t); Node* chanfn(char *name, int n, Type *t); -void copytype(Node *n, Type *t); -void defertypecopy(Node *n, Type *t); Node* mkcall(char *name, Type *t, NodeList **init, ...); Node* mkcall1(Node *fn, Type *t, NodeList **init, ...); -void queuemethod(Node *n); -void resumetypecopy(void); int vmatch1(Node *l, Node *r); void walk(Node *fn); -Node* walkdef(Node *n); void walkexpr(Node **np, NodeList **init); void walkexprlist(NodeList *l, NodeList **init); void walkexprlistsafe(NodeList *l, NodeList **init); diff --git a/src/cmd/gc/go.y b/src/cmd/gc/go.y index fdaab4fa4..1278c2586 100644 --- a/src/cmd/gc/go.y +++ b/src/cmd/gc/go.y @@ -1792,24 +1792,12 @@ hidden_opt_sym: } hidden_dcl: - hidden_opt_sym hidden_type + hidden_opt_sym hidden_type hidden_tag { $$ = nod(ODCLFIELD, $1, typenod($2)); + $$->val = $3; } -| hidden_opt_sym LDDD - { - Type *t; - - yyerror("invalid variadic function type in import - recompile import"); - - t = typ(TARRAY); - t->bound = -1; - t->type = typ(TINTER); - $$ = nod(ODCLFIELD, $1, typenod(t)); - $$->isddd = 1; - } - -| hidden_opt_sym LDDD hidden_type +| hidden_opt_sym LDDD hidden_type hidden_tag { Type *t; @@ -1818,6 +1806,7 @@ hidden_dcl: t->type = $3; $$ = nod(ODCLFIELD, $1, typenod(t)); $$->isddd = 1; + $$->val = $4; } hidden_structdcl: diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c index 5e2f73fc5..88acb60af 100644 --- a/src/cmd/gc/lex.c +++ b/src/cmd/gc/lex.c @@ -5,7 +5,7 @@ #define EXTERN #include "go.h" #include "y.tab.h" -#include <ar.h> +#include <ar.h> #undef getc #undef ungetc @@ -274,7 +274,9 @@ main(int argc, char *argv[]) funccompile(l->n, 1); } - dclchecks(); + for(l=externdcl; l; l=l->next) + if(l->n->op == ONAME) + typecheck(&l->n, Erv); if(nerrors) errorexit(); diff --git a/src/cmd/gc/pgen.c b/src/cmd/gc/pgen.c index 9bd845dde..ab6186697 100644 --- a/src/cmd/gc/pgen.c +++ b/src/cmd/gc/pgen.c @@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#undef EXTERN -#define EXTERN -#include "gg.h" -#include "opt.h" +#include "gg.h" +#include "opt.h" + +static void compactframe(Prog* p); void compile(Node *fn) @@ -16,6 +16,7 @@ compile(Node *fn) int32 lno; Type *t; Iter save; + vlong oldstksize; if(newproc == N) { newproc = sysfunc("newproc"); @@ -109,11 +110,115 @@ compile(Node *fn) regopt(ptxt); } + oldstksize = stksize; + if(thechar != '5') + compactframe(ptxt); + if(0) + print("compactframe: %ld to %ld\n", oldstksize, stksize); + defframe(ptxt); - if(debug['f']) + if(0) frame(0); ret: lineno = lno; } + + +// Sort the list of stack variables. autos after anything else, +// within autos, unused after used, and within used on reverse alignment. +// non-autos sort on offset. +static int +cmpstackvar(Node *a, Node *b) +{ + if (a->class != b->class) + return (a->class == PAUTO) ? 1 : -1; + if (a->class != PAUTO) + return a->xoffset - b->xoffset; + if ((a->used == 0) != (b->used == 0)) + return b->used - a->used; + return b->type->align - a->type->align; + +} + +static void +compactframe(Prog* ptxt) +{ + NodeList *ll; + Node* n; + Prog *p; + uint32 w; + + if (stksize == 0) + return; + + // Mark the PAUTO's unused. + for(ll=curfn->dcl; ll != nil; ll=ll->next) + if (ll->n->class == PAUTO && ll->n->op == ONAME) + ll->n->used = 0; + + // Sweep the prog list to mark any used nodes. + for (p = ptxt; p; p = p->link) { + if (p->from.type == D_AUTO && p->from.node) + p->from.node->used++; + + if (p->to.type == D_AUTO && p->to.node) + p->to.node->used++; + } + + listsort(&curfn->dcl, cmpstackvar); + + // Unused autos are at the end, chop 'em off. + ll = curfn->dcl; + n = ll->n; + if (n->class == PAUTO && n->op == ONAME && !n->used) { + curfn->dcl = nil; + stksize = 0; + return; + } + + for(ll = curfn->dcl; ll->next != nil; ll=ll->next) { + n = ll->next->n; + if (n->class == PAUTO && n->op == ONAME && !n->used) { + ll->next = nil; + curfn->dcl->end = ll; + break; + } + } + + // Reassign stack offsets of the locals that are still there. + stksize = 0; + for(ll = curfn->dcl; ll != nil; ll=ll->next) { + n = ll->n; + // TODO find out where the literal autos come from + if (n->class != PAUTO || n->op != ONAME) + continue; + + w = n->type->width; + if((w >= MAXWIDTH) || (w < 1)) + fatal("bad width"); + stksize += w; + stksize = rnd(stksize, n->type->align); + if(thechar == '5') + stksize = rnd(stksize, widthptr); + n->stkdelta = -stksize - n->xoffset; + } + + // Fixup instructions. + for (p = ptxt; p; p = p->link) { + if (p->from.type == D_AUTO && p->from.node) + p->from.offset += p->from.node->stkdelta; + + if (p->to.type == D_AUTO && p->to.node) + p->to.offset += p->to.node->stkdelta; + } + + // The debug information needs accurate offsets on the symbols. + for(ll = curfn->dcl ;ll != nil; ll=ll->next) { + if (ll->n->class != PAUTO || ll->n->op != ONAME) + continue; + ll->n->xoffset += ll->n->stkdelta; + ll->n->stkdelta = 0; + } +} diff --git a/src/cmd/gc/print.c b/src/cmd/gc/print.c index e03a14080..e88e0f844 100644 --- a/src/cmd/gc/print.c +++ b/src/cmd/gc/print.c @@ -134,6 +134,10 @@ exprfmt(Fmt *f, Node *n, int prec) fmtprint(f, "(node %O)", n->op); break; + case OREGISTER: + fmtprint(f, "%R", n->val.u.reg); + break; + case OLITERAL: if(n->sym != S) { fmtprint(f, "%S", n->sym); diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c index 4c0819cd8..49797f9df 100644 --- a/src/cmd/gc/subr.c +++ b/src/cmd/gc/subr.c @@ -105,7 +105,7 @@ hcrash(void) flusherrors(); if(outfile) unlink(outfile); - *(int*)0 = 0; + *(volatile int*)0 = 0; } } @@ -480,6 +480,7 @@ nod(int op, Node *nleft, Node *nright) n->right = nright; n->lineno = parserline(); n->xoffset = BADWIDTH; + n->orig = n; return n; } @@ -1031,10 +1032,21 @@ Econv(Fmt *fp) return fmtstrcpy(fp, etnames[et]); } +static const char* classnames[] = { + "Pxxx", + "PEXTERN", + "PAUTO", + "PPARAM", + "PPARAMOUT", + "PPARAMREF", + "PFUNC", +}; + int Jconv(Fmt *fp) { Node *n; + char *s; n = va_arg(fp->args, Node*); if(n->ullman != 0) @@ -1049,12 +1061,18 @@ Jconv(Fmt *fp) if(n->lineno != 0) fmtprint(fp, " l(%d)", n->lineno); - if(n->xoffset != 0) - fmtprint(fp, " x(%lld)", n->xoffset); - - if(n->class != 0) - fmtprint(fp, " class(%d)", n->class); + if(n->xoffset != BADWIDTH) + fmtprint(fp, " x(%lld%+d)", n->xoffset, n->stkdelta); + if(n->class != 0) { + s = ""; + if (n->class & PHEAP) s = ",heap"; + if ((n->class & ~PHEAP) < nelem(classnames)) + fmtprint(fp, " class(%s%s)", classnames[n->class&~PHEAP], s); + else + fmtprint(fp, " class(%d?%s)", n->class&~PHEAP, s); + } + if(n->colas != 0) fmtprint(fp, " colas(%d)", n->colas); @@ -1076,6 +1094,8 @@ Jconv(Fmt *fp) if(n->pun != 0) fmtprint(fp, " pun(%d)", n->pun); + if(n->used != 0) + fmtprint(fp, " used(%d)", n->used); return 0; } @@ -3339,6 +3359,64 @@ list(NodeList *l, Node *n) return concat(l, list1(n)); } +void +listsort(NodeList** l, int(*f)(Node*, Node*)) +{ + NodeList *l1, *l2, *le; + + if(*l == nil || (*l)->next == nil) + return; + + l1 = *l; + l2 = *l; + for(;;) { + l2 = l2->next; + if(l2 == nil) + break; + l2 = l2->next; + if(l2 == nil) + break; + l1 = l1->next; + } + + l2 = l1->next; + l1->next = nil; + l2->end = (*l)->end; + (*l)->end = l1; + + l1 = *l; + listsort(&l1, f); + listsort(&l2, f); + + if ((*f)(l1->n, l2->n) < 0) { + *l = l1; + } else { + *l = l2; + l2 = l1; + l1 = *l; + } + + // now l1 == *l; and l1 < l2 + + while ((l1 != nil) && (l2 != nil)) { + while ((l1->next != nil) && (*f)(l1->next->n, l2->n) < 0) + l1 = l1->next; + + // l1 is last one from l1 that is < l2 + le = l1->next; // le is the rest of l1, first one that is >= l2 + if (le != nil) + le->end = (*l)->end; + + (*l)->end = l1; // cut *l at l1 + *l = concat(*l, l2); // glue l2 to *l's tail + + l1 = l2; // l1 is the first element of *l that is < the new l2 + l2 = le; // ... because l2 now is the old tail of l1 + } + + *l = concat(*l, l2); // any remainder +} + NodeList* listtreecopy(NodeList *l) { diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c index 44d08352d..04dc1a507 100644 --- a/src/cmd/gc/typecheck.c +++ b/src/cmd/gc/typecheck.c @@ -8,7 +8,6 @@ * evaluates compile time constants. * marks variables that escape the local frame. * rewrites n->op to be more specific in some cases. - * sets n->walk to walking function. */ #include "go.h" @@ -33,6 +32,8 @@ static void stringtoarraylit(Node**); static Node* resolve(Node*); static Type* getforwtype(Node*); +static NodeList* typecheckdefstack; + /* * resolve ONONAME to definition, if any. */ @@ -159,7 +160,7 @@ typecheck(Node **np, int top) if(n->op == OTYPE && (ft = getforwtype(n->ntype)) != T) defertypecopy(n, ft); - walkdef(n); + typecheckdef(n); n->realtype = n->type; if(n->op == ONONAME) goto error; @@ -2523,3 +2524,294 @@ getforwtype(Node *n) } } } + +static int ntypecheckdeftype; +static NodeList *methodqueue; + +static void +domethod(Node *n) +{ + Node *nt; + + nt = n->type->nname; + typecheck(&nt, Etype); + if(nt->type == T) { + // type check failed; leave empty func + n->type->etype = TFUNC; + n->type->nod = N; + return; + } + *n->type = *nt->type; + n->type->nod = N; + checkwidth(n->type); +} + +typedef struct NodeTypeList NodeTypeList; +struct NodeTypeList { + Node *n; + Type *t; + NodeTypeList *next; +}; + +static NodeTypeList *dntq; +static NodeTypeList *dntend; + +void +defertypecopy(Node *n, Type *t) +{ + NodeTypeList *ntl; + + if(n == N || t == T) + return; + + ntl = mal(sizeof *ntl); + ntl->n = n; + ntl->t = t; + ntl->next = nil; + + if(dntq == nil) + dntq = ntl; + else + dntend->next = ntl; + + dntend = ntl; +} + +void +resumetypecopy(void) +{ + NodeTypeList *l; + + for(l=dntq; l; l=l->next) + copytype(l->n, l->t); +} + +void +copytype(Node *n, Type *t) +{ + *n->type = *t; + + t = n->type; + t->sym = n->sym; + t->local = n->local; + t->vargen = n->vargen; + t->siggen = 0; + t->method = nil; + t->nod = N; + t->printed = 0; + t->deferwidth = 0; +} + +static void +typecheckdeftype(Node *n) +{ + int maplineno, embedlineno, lno; + Type *t; + NodeList *l; + + ntypecheckdeftype++; + lno = lineno; + setlineno(n); + n->type->sym = n->sym; + n->typecheck = 1; + typecheck(&n->ntype, Etype); + if((t = n->ntype->type) == T) { + n->diag = 1; + goto ret; + } + if(n->type == T) { + n->diag = 1; + goto ret; + } + + maplineno = n->type->maplineno; + embedlineno = n->type->embedlineno; + + // copy new type and clear fields + // that don't come along. + // anything zeroed here must be zeroed in + // typedcl2 too. + copytype(n, t); + + // double-check use of type as map key. + if(maplineno) { + lineno = maplineno; + maptype(n->type, types[TBOOL]); + } + if(embedlineno) { + lineno = embedlineno; + if(isptr[t->etype]) + yyerror("embedded type cannot be a pointer"); + } + +ret: + lineno = lno; + + // if there are no type definitions going on, it's safe to + // try to resolve the method types for the interfaces + // we just read. + if(ntypecheckdeftype == 1) { + while((l = methodqueue) != nil) { + methodqueue = nil; + for(; l; l=l->next) + domethod(l->n); + } + } + ntypecheckdeftype--; +} + +void +queuemethod(Node *n) +{ + if(ntypecheckdeftype == 0) { + domethod(n); + return; + } + methodqueue = list(methodqueue, n); +} + +Node* +typecheckdef(Node *n) +{ + int lno; + Node *e; + Type *t; + NodeList *l; + + lno = lineno; + setlineno(n); + + if(n->op == ONONAME) { + if(!n->diag) { + n->diag = 1; + if(n->lineno != 0) + lineno = n->lineno; + yyerror("undefined: %S", n->sym); + } + return n; + } + + if(n->walkdef == 1) + return n; + + l = mal(sizeof *l); + l->n = n; + l->next = typecheckdefstack; + typecheckdefstack = l; + + if(n->walkdef == 2) { + flusherrors(); + print("typecheckdef loop:"); + for(l=typecheckdefstack; l; l=l->next) + print(" %S", l->n->sym); + print("\n"); + fatal("typecheckdef loop"); + } + n->walkdef = 2; + + if(n->type != T || n->sym == S) // builtin or no name + goto ret; + + switch(n->op) { + default: + fatal("typecheckdef %O", n->op); + + case OLITERAL: + if(n->ntype != N) { + typecheck(&n->ntype, Etype); + n->type = n->ntype->type; + n->ntype = N; + if(n->type == T) { + n->diag = 1; + goto ret; + } + } + e = n->defn; + n->defn = N; + if(e == N) { + lineno = n->lineno; + dump("typecheckdef nil defn", n); + yyerror("xxx"); + } + typecheck(&e, Erv | Eiota); + if(e->type != T && e->op != OLITERAL) { + yyerror("const initializer must be constant"); + goto ret; + } + if(isconst(e, CTNIL)) { + yyerror("const initializer cannot be nil"); + goto ret; + } + t = n->type; + if(t != T) { + if(!okforconst[t->etype]) { + yyerror("invalid constant type %T", t); + goto ret; + } + if(!isideal(e->type) && !eqtype(t, e->type)) { + yyerror("cannot use %+N as type %T in const initializer", e, t); + goto ret; + } + convlit(&e, t); + } + n->val = e->val; + n->type = e->type; + break; + + case ONAME: + if(n->ntype != N) { + typecheck(&n->ntype, Etype); + n->type = n->ntype->type; + if(n->type == T) { + n->diag = 1; + goto ret; + } + } + if(n->type != T) + break; + if(n->defn == N) { + if(n->etype != 0) // like OPRINTN + break; + if(nerrors > 0) { + // Can have undefined variables in x := foo + // that make x have an n->ndefn == nil. + // If there are other errors anyway, don't + // bother adding to the noise. + break; + } + fatal("var without type, init: %S", n->sym); + } + if(n->defn->op == ONAME) { + typecheck(&n->defn, Erv); + n->type = n->defn->type; + break; + } + typecheck(&n->defn, Etop); // fills in n->type + break; + + case OTYPE: + if(curfn) + defercheckwidth(); + n->walkdef = 1; + n->type = typ(TFORW); + n->type->sym = n->sym; + typecheckdeftype(n); + if(curfn) + resumecheckwidth(); + break; + + case OPACK: + // nothing to see here + break; + } + +ret: + if(typecheckdefstack->n != n) + fatal("typecheckdefstack mismatch"); + l = typecheckdefstack; + typecheckdefstack = l->next; + + lineno = lno; + n->walkdef = 1; + return n; +} diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c index ccc65ff21..65a504bff 100644 --- a/src/cmd/gc/walk.c +++ b/src/cmd/gc/walk.c @@ -21,8 +21,6 @@ static Node* addstr(Node*, NodeList**); static Node* appendslice(Node*, NodeList**); static Node* append(Node*, NodeList**); -static NodeList* walkdefstack; - // can this code branch reach the end // without an unconditional RETURN // this is hard, so it is conservative @@ -100,296 +98,6 @@ walk(Node *fn) } } -static int nwalkdeftype; -static NodeList *methodqueue; - -static void -domethod(Node *n) -{ - Node *nt; - - nt = n->type->nname; - typecheck(&nt, Etype); - if(nt->type == T) { - // type check failed; leave empty func - n->type->etype = TFUNC; - n->type->nod = N; - return; - } - *n->type = *nt->type; - n->type->nod = N; - checkwidth(n->type); -} - -typedef struct NodeTypeList NodeTypeList; -struct NodeTypeList { - Node *n; - Type *t; - NodeTypeList *next; -}; - -static NodeTypeList *dntq; -static NodeTypeList *dntend; - -void -defertypecopy(Node *n, Type *t) -{ - NodeTypeList *ntl; - - if(n == N || t == T) - return; - - ntl = mal(sizeof *ntl); - ntl->n = n; - ntl->t = t; - ntl->next = nil; - - if(dntq == nil) - dntq = ntl; - else - dntend->next = ntl; - - dntend = ntl; -} - -void -resumetypecopy(void) -{ - NodeTypeList *l; - - for(l=dntq; l; l=l->next) - copytype(l->n, l->t); -} - -void -copytype(Node *n, Type *t) -{ - *n->type = *t; - - t = n->type; - t->sym = n->sym; - t->local = n->local; - t->vargen = n->vargen; - t->siggen = 0; - t->method = nil; - t->nod = N; - t->printed = 0; - t->deferwidth = 0; -} - -static void -walkdeftype(Node *n) -{ - int maplineno, embedlineno, lno; - Type *t; - NodeList *l; - - nwalkdeftype++; - lno = lineno; - setlineno(n); - n->type->sym = n->sym; - n->typecheck = 1; - typecheck(&n->ntype, Etype); - if((t = n->ntype->type) == T) { - n->diag = 1; - goto ret; - } - if(n->type == T) { - n->diag = 1; - goto ret; - } - - maplineno = n->type->maplineno; - embedlineno = n->type->embedlineno; - - // copy new type and clear fields - // that don't come along. - // anything zeroed here must be zeroed in - // typedcl2 too. - copytype(n, t); - - // double-check use of type as map key. - if(maplineno) { - lineno = maplineno; - maptype(n->type, types[TBOOL]); - } - if(embedlineno) { - lineno = embedlineno; - if(isptr[t->etype]) - yyerror("embedded type cannot be a pointer"); - } - -ret: - lineno = lno; - - // if there are no type definitions going on, it's safe to - // try to resolve the method types for the interfaces - // we just read. - if(nwalkdeftype == 1) { - while((l = methodqueue) != nil) { - methodqueue = nil; - for(; l; l=l->next) - domethod(l->n); - } - } - nwalkdeftype--; -} - -void -queuemethod(Node *n) -{ - if(nwalkdeftype == 0) { - domethod(n); - return; - } - methodqueue = list(methodqueue, n); -} - -Node* -walkdef(Node *n) -{ - int lno; - Node *e; - Type *t; - NodeList *l; - - lno = lineno; - setlineno(n); - - if(n->op == ONONAME) { - if(!n->diag) { - n->diag = 1; - if(n->lineno != 0) - lineno = n->lineno; - yyerror("undefined: %S", n->sym); - } - return n; - } - - if(n->walkdef == 1) - return n; - - l = mal(sizeof *l); - l->n = n; - l->next = walkdefstack; - walkdefstack = l; - - if(n->walkdef == 2) { - flusherrors(); - print("walkdef loop:"); - for(l=walkdefstack; l; l=l->next) - print(" %S", l->n->sym); - print("\n"); - fatal("walkdef loop"); - } - n->walkdef = 2; - - if(n->type != T || n->sym == S) // builtin or no name - goto ret; - - switch(n->op) { - default: - fatal("walkdef %O", n->op); - - case OLITERAL: - if(n->ntype != N) { - typecheck(&n->ntype, Etype); - n->type = n->ntype->type; - n->ntype = N; - if(n->type == T) { - n->diag = 1; - goto ret; - } - } - e = n->defn; - n->defn = N; - if(e == N) { - lineno = n->lineno; - dump("walkdef nil defn", n); - yyerror("xxx"); - } - typecheck(&e, Erv | Eiota); - if(e->type != T && e->op != OLITERAL) { - yyerror("const initializer must be constant"); - goto ret; - } - if(isconst(e, CTNIL)) { - yyerror("const initializer cannot be nil"); - goto ret; - } - t = n->type; - if(t != T) { - if(!okforconst[t->etype]) { - yyerror("invalid constant type %T", t); - goto ret; - } - if(!isideal(e->type) && !eqtype(t, e->type)) { - yyerror("cannot use %+N as type %T in const initializer", e, t); - goto ret; - } - convlit(&e, t); - } - n->val = e->val; - n->type = e->type; - break; - - case ONAME: - if(n->ntype != N) { - typecheck(&n->ntype, Etype); - n->type = n->ntype->type; - if(n->type == T) { - n->diag = 1; - goto ret; - } - } - if(n->type != T) - break; - if(n->defn == N) { - if(n->etype != 0) // like OPRINTN - break; - if(nerrors > 0) { - // Can have undefined variables in x := foo - // that make x have an n->ndefn == nil. - // If there are other errors anyway, don't - // bother adding to the noise. - break; - } - fatal("var without type, init: %S", n->sym); - } - if(n->defn->op == ONAME) { - typecheck(&n->defn, Erv); - n->type = n->defn->type; - break; - } - typecheck(&n->defn, Etop); // fills in n->type - break; - - case OTYPE: - if(curfn) - defercheckwidth(); - n->walkdef = 1; - n->type = typ(TFORW); - n->type->sym = n->sym; - walkdeftype(n); - if(curfn) - resumecheckwidth(); - break; - - case OPACK: - // nothing to see here - break; - } - -ret: - if(walkdefstack->n != n) - fatal("walkdefstack mismatch"); - l = walkdefstack; - walkdefstack = l->next; - - lineno = lno; - n->walkdef = 1; - return n; -} void walkstmtlist(NodeList *l) diff --git a/src/cmd/godefs/a.h b/src/cmd/godefs/a.h index 03ab91f65..9b4957467 100644 --- a/src/cmd/godefs/a.h +++ b/src/cmd/godefs/a.h @@ -75,6 +75,7 @@ extern Const *con; extern int ncon; extern Type **typ; extern int ntyp; +extern int kindsize[]; // Language output typedef struct Lang Lang; diff --git a/src/cmd/godefs/main.c b/src/cmd/godefs/main.c index d4163421d..6a8630179 100644 --- a/src/cmd/godefs/main.c +++ b/src/cmd/godefs/main.c @@ -181,7 +181,7 @@ main(int argc, char **argv) char **av, *q, *r, *tofree, *name; char nambuf[100]; Biobuf *bin, *bout; - Type *t; + Type *t, *tt; Field *f; int orig_output_fd; @@ -373,8 +373,16 @@ Continue: prefix = prefixlen(t); for(j=0; j<t->nf; j++) { f = &t->f[j]; - if(f->type->kind == 0) - continue; + if(f->type->kind == 0 && f->size <= 64 && (f->size&(f->size-1)) == 0) { + // unknown type but <= 64 bits and bit size is a power of two. + // could be enum - make Uint64 and then let it reduce + tt = emalloc(sizeof *tt); + *tt = *f->type; + f->type = tt; + tt->kind = Uint64; + while(tt->kind > Uint8 && kindsize[tt->kind] > f->size) + tt->kind -= 2; + } // padding if(t->kind == Struct || lang == &go) { if(f->offset%8 != 0 || f->size%8 != 0) { diff --git a/src/cmd/godefs/stabs.c b/src/cmd/godefs/stabs.c index 30a05fc70..2c3d431b8 100644 --- a/src/cmd/godefs/stabs.c +++ b/src/cmd/godefs/stabs.c @@ -149,7 +149,7 @@ Intrange intranges[] = { 16, 0, Void, }; -static int kindsize[] = { +int kindsize[] = { 0, 0, 8, @@ -381,14 +381,6 @@ parsedef(char **pp, char *name) while(f->type->kind == Typedef) f->type = f->type->type; - if(f->type->kind == 0 && f->size <= 64 && (f->size&(f->size-1)) == 0) { - // unknown type but <= 64 bits and bit size is a power of two. - // could be enum - make Uint64 and then let it reduce - tt = emalloc(sizeof *tt); - *tt = *f->type; - f->type = tt; - tt->kind = Uint64; - } // rewrite // uint32 x : 8; diff --git a/src/cmd/godoc/spec.go b/src/cmd/godoc/spec.go index d863ca0d8..444e36e08 100644 --- a/src/cmd/godoc/spec.go +++ b/src/cmd/godoc/spec.go @@ -129,6 +129,9 @@ func (p *ebnfParser) parseTerm() bool { func (p *ebnfParser) parseSequence() { + if !p.parseTerm() { + p.errorExpected(p.pos, "term") + } for p.parseTerm() { } } @@ -148,7 +151,9 @@ func (p *ebnfParser) parseExpression() { func (p *ebnfParser) parseProduction() { p.parseIdentifier(true) p.expect(token.ASSIGN) - p.parseExpression() + if p.tok != token.PERIOD { + p.parseExpression() + } p.expect(token.PERIOD) } diff --git a/src/cmd/gofix/main.go b/src/cmd/gofix/main.go index ba2061a00..1b091c18a 100644 --- a/src/cmd/gofix/main.go +++ b/src/cmd/gofix/main.go @@ -248,5 +248,11 @@ func diff(b1, b2 []byte) (data []byte, err os.Error) { f1.Write(b1) f2.Write(b2) - return exec.Command("diff", f1.Name(), f2.Name()).CombinedOutput() + data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput() + if len(data) > 0 { + // diff exits with a non-zero status when the files don't match. + // Ignore that failure as long as we get output. + err = nil + } + return } diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go index 16bcd3c4d..ea1c1b00f 100644 --- a/src/cmd/gofmt/gofmt.go +++ b/src/cmd/gofmt/gofmt.go @@ -260,5 +260,12 @@ func diff(b1, b2 []byte) (data []byte, err os.Error) { f1.Write(b1) f2.Write(b2) - return exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput() + data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput() + if len(data) > 0 { + // diff exits with a non-zero status when the files don't match. + // Ignore that failure as long as we get output. + err = nil + } + return + } diff --git a/src/cmd/gofmt/test.sh b/src/cmd/gofmt/test.sh index f60ff9b32..5dce2ed7a 100755 --- a/src/cmd/gofmt/test.sh +++ b/src/cmd/gofmt/test.sh @@ -36,7 +36,7 @@ apply1() { # the following files are skipped because they are test cases # for syntax errors and thus won't parse in the first place: case `basename "$F"` in - func3.go | const2.go | char_lit1.go | blank1.go | \ + func3.go | const2.go | char_lit1.go | blank1.go | ddd1.go | \ bug014.go | bug050.go | bug068.go | bug083.go | bug088.go | \ bug106.go | bug121.go | bug125.go | bug133.go | bug160.go | \ bug163.go | bug166.go | bug169.go | bug217.go | bug222.go | \ diff --git a/src/cmd/gopack/ar.c b/src/cmd/gopack/ar.c index 017978ced..0b5e608c7 100644 --- a/src/cmd/gopack/ar.c +++ b/src/cmd/gopack/ar.c @@ -1532,25 +1532,7 @@ arwrite(int fd, Armember *bp) int page(Arfile *ap) { - Armember *bp; - - bp = ap->head; - if (!ap->paged) { /* not yet paged - create file */ - ap->fname = mktemp(ap->fname); - ap->fd = create(ap->fname, ORDWR|ORCLOSE, 0600); - if (ap->fd < 0) { - fprint(2,"gopack: can't create temp file\n"); - return 0; - } - ap->paged = 1; - } - if (!arwrite(ap->fd, bp)) /* write member and free buffer block */ - return 0; - ap->head = bp->next; - if (ap->tail == bp) - ap->tail = bp->next; - free(bp->member); - free(bp); + sysfatal("page"); return 1; } diff --git a/src/cmd/ld/dwarf.c b/src/cmd/ld/dwarf.c index de600f555..1721def67 100644 --- a/src/cmd/ld/dwarf.c +++ b/src/cmd/ld/dwarf.c @@ -1348,7 +1348,7 @@ synthesizemaptypes(DWDie *die) valtype = defptrto(valtype); newrefattr(fld, DW_AT_type, valtype); newmemberoffsetattr(fld, hashsize + datavo); - newattr(dwhe, DW_AT_byte_size, DW_CLS_CONSTANT, hashsize + datsize, NULL); + newattr(dwhe, DW_AT_byte_size, DW_CLS_CONSTANT, hashsize + datsize, nil); // Construct hash_subtable<hash_entry<K,V>> dwhs = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, @@ -1359,7 +1359,7 @@ synthesizemaptypes(DWDie *die) substitutetype(dwhs, "end", defptrto(dwhe)); substitutetype(dwhs, "entry", dwhe); // todo: []hash_entry with dynamic size newattr(dwhs, DW_AT_byte_size, DW_CLS_CONSTANT, - getattr(hash_subtable, DW_AT_byte_size)->value, NULL); + getattr(hash_subtable, DW_AT_byte_size)->value, nil); // Construct hash<K,V> dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, @@ -1369,7 +1369,7 @@ synthesizemaptypes(DWDie *die) copychildren(dwh, hash); substitutetype(dwh, "st", defptrto(dwhs)); newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, - getattr(hash, DW_AT_byte_size)->value, NULL); + getattr(hash, DW_AT_byte_size)->value, nil); newrefattr(die, DW_AT_type, defptrto(dwh)); } @@ -1401,30 +1401,30 @@ synthesizechantypes(DWDie *die) // sudog<T> dws = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, mkinternaltypename("sudog", - getattr(elemtype, DW_AT_name)->data, NULL)); + getattr(elemtype, DW_AT_name)->data, nil)); copychildren(dws, sudog); substitutetype(dws, "elem", elemtype); newattr(dws, DW_AT_byte_size, DW_CLS_CONSTANT, - sudogsize + (elemsize > 8 ? elemsize - 8 : 0), NULL); + sudogsize + (elemsize > 8 ? elemsize - 8 : 0), nil); // waitq<T> dww = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, - mkinternaltypename("waitq", getattr(elemtype, DW_AT_name)->data, NULL)); + mkinternaltypename("waitq", getattr(elemtype, DW_AT_name)->data, nil)); copychildren(dww, waitq); substitutetype(dww, "first", defptrto(dws)); substitutetype(dww, "last", defptrto(dws)); newattr(dww, DW_AT_byte_size, DW_CLS_CONSTANT, - getattr(waitq, DW_AT_byte_size)->value, NULL); + getattr(waitq, DW_AT_byte_size)->value, nil); // hchan<T> dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, - mkinternaltypename("hchan", getattr(elemtype, DW_AT_name)->data, NULL)); + mkinternaltypename("hchan", getattr(elemtype, DW_AT_name)->data, nil)); copychildren(dwh, hchan); substitutetype(dwh, "recvq", dww); substitutetype(dwh, "sendq", dww); substitutetype(dwh, "free", defptrto(dws)); newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, - getattr(hchan, DW_AT_byte_size)->value, NULL); + getattr(hchan, DW_AT_byte_size)->value, nil); newrefattr(die, DW_AT_type, defptrto(dwh)); } @@ -1436,6 +1436,7 @@ defdwsymb(Sym* sym, char *s, int t, vlong v, vlong size, int ver, Sym *gotype) { DWDie *dv, *dt; + USED(size); if (strncmp(s, "go.string.", 10) == 0) return; @@ -1592,7 +1593,7 @@ addhistfile(char *zentry) // if the histfile stack contains ..../runtime/runtime_defs.go // use that to set gdbscript static void -finddebugruntimepath() +finddebugruntimepath(void) { int i, l; char *c; @@ -1841,7 +1842,8 @@ writelines(void) char *n, *nn; unitstart = -1; - epc = pc = 0; + pc = 0; + epc = 0; lc = 1; llc = 1; currfile = -1; @@ -1903,7 +1905,8 @@ writelines(void) // 4 zeros: the string termination + 3 fields. } - epc = pc = s->text->pc; + pc = s->text->pc; + epc = pc; currfile = 1; lc = 1; llc = 1; @@ -1992,7 +1995,7 @@ writelines(void) newrefattr(dwvar, DW_AT_type, defgotype(a->gotype)); // push dwvar down dwfunc->child to preserve order - newattr(dwvar, DW_AT_internal_location, DW_CLS_CONSTANT, offs, NULL); + newattr(dwvar, DW_AT_internal_location, DW_CLS_CONSTANT, offs, nil); dwfunc->child = dwvar->link; // take dwvar out from the top of the list for (dws = &dwfunc->child; *dws != nil; dws = &(*dws)->link) if (offs > getattr(*dws, DW_AT_internal_location)->value) @@ -2345,7 +2348,11 @@ dwarfemitdebugsections(void) infoo = cpos(); writeinfo(); - gdbscripto = arangeso = pubtypeso = pubnameso = infoe = cpos(); + infoe = cpos(); + pubnameso = infoe; + pubtypeso = infoe; + arangeso = infoe; + gdbscripto = infoe; if (fwdcount > 0) { if (debug['v']) diff --git a/src/cmd/ld/go.c b/src/cmd/ld/go.c index a19fe460d..05d1cc136 100644 --- a/src/cmd/ld/go.c +++ b/src/cmd/ld/go.c @@ -415,8 +415,8 @@ loaddynimport(char *file, char *pkg, char *p, int n) char *pend, *next, *name, *def, *p0, *lib, *q; Sym *s; + USED(file); pend = p + n; - p0 = p; for(; p<pend; p=next) { next = strchr(p, '\n'); if(next == nil) @@ -485,8 +485,8 @@ loaddynexport(char *file, char *pkg, char *p, int n) char *pend, *next, *local, *elocal, *remote, *p0; Sym *s; + USED(file); pend = p + n; - p0 = p; for(; p<pend; p=next) { next = strchr(p, '\n'); if(next == nil) diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c index 105d982e4..04ee790a4 100644 --- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -69,7 +69,7 @@ libinit(void) // add goroot to the end of the libdir list. libdir[nlibdir++] = smprint("%s/pkg/%s_%s", goroot, goos, goarch); - unlink(outfile); + remove(outfile); cout = create(outfile, 1, 0775); if(cout < 0) { diag("cannot create %s", outfile); @@ -235,24 +235,34 @@ addlibpath(char *srcref, char *objref, char *file, char *pkg) } void -loadlib(void) +loadinternal(char *name) { char pname[1024]; int i, found; found = 0; for(i=0; i<nlibdir; i++) { - snprint(pname, sizeof pname, "%s/runtime.a", libdir[i]); + snprint(pname, sizeof pname, "%s/%s.a", libdir[i], name); if(debug['v']) - Bprint(&bso, "searching for runtime.a in %s\n", pname); + Bprint(&bso, "searching for %s.a in %s\n", name, pname); if(access(pname, AEXIST) >= 0) { - addlibpath("internal", "internal", pname, "runtime"); + addlibpath("internal", "internal", pname, name); found = 1; break; } } if(!found) - Bprint(&bso, "warning: unable to find runtime.a\n"); + Bprint(&bso, "warning: unable to find %s.a\n", name); +} + +void +loadlib(void) +{ + int i; + + loadinternal("runtime"); + if(thechar == '5') + loadinternal("math"); for(i=0; i<libraryp; i++) { if(debug['v']) @@ -398,9 +408,6 @@ ldobj(Biobuf *f, char *pkg, int64 len, char *pn, int whence) eof = Boffset(f) + len; pn = strdup(pn); - - USED(c4); - USED(magic); c1 = Bgetc(f); c2 = Bgetc(f); @@ -410,7 +417,7 @@ ldobj(Biobuf *f, char *pkg, int64 len, char *pn, int whence) Bungetc(f); Bungetc(f); Bungetc(f); - + magic = c1<<24 | c2<<16 | c3<<8 | c4; if(magic == 0x7f454c46) { // \x7F E L F ldelf(f, pkg, len, pn); @@ -498,7 +505,6 @@ _lookup(char *symb, int v, int creat) // not if(h < 0) h = ~h, because gcc 4.3 -O2 miscompiles it. h &= 0xffffff; h %= NHASH; - c = symb[0]; for(s = hash[h]; s != S; s = s->hash) if(memcmp(s->name, symb, l) == 0) return s; @@ -523,7 +529,7 @@ _lookup(char *symb, int v, int creat) s->size = 0; hash[h] = s; nsymbol++; - + s->allsym = allsym; allsym = s; return s; @@ -550,7 +556,6 @@ copyhistfrog(char *buf, int nbuf) p = buf; ep = buf + nbuf; - i = 0; for(i=0; i<histfrogp; i++) { p = seprint(p, ep, "%s", histfrog[i]->name+1); if(i+1<histfrogp && (p == buf || p[-1] != '/')) diff --git a/src/cmd/ld/lib.h b/src/cmd/ld/lib.h index f69f5a35d..dfd18fbff 100644 --- a/src/cmd/ld/lib.h +++ b/src/cmd/ld/lib.h @@ -186,8 +186,7 @@ vlong addsize(Sym*, Sym*); vlong adduint8(Sym*, uint8); vlong adduint16(Sym*, uint16); void asmsym(void); -void asmelfsym32(void); -void asmelfsym64(void); +void asmelfsym(void); void asmplan9sym(void); void strnput(char*, int); void dodata(void); diff --git a/src/cmd/ld/symtab.c b/src/cmd/ld/symtab.c index da698fcc0..e3093b2aa 100644 --- a/src/cmd/ld/symtab.c +++ b/src/cmd/ld/symtab.c @@ -61,49 +61,35 @@ putelfstr(char *s) } void -putelfsym64(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) +putelfsyment(int off, vlong addr, vlong size, int info, int shndx) { - int bind, type, shndx, stroff; - - bind = STB_GLOBAL; - switch(t) { - default: - return; - case 'T': - type = STT_FUNC; - shndx = elftextsh + 0; - break; - case 'D': - type = STT_OBJECT; - shndx = elftextsh + 1; + switch(thechar) { + case '6': + LPUT(off); + cput(info); + cput(0); + WPUT(shndx); + VPUT(addr); + VPUT(size); + symsize += ELF64SYMSIZE; break; - case 'B': - type = STT_OBJECT; - shndx = elftextsh + 2; + default: + LPUT(off); + LPUT(addr); + LPUT(size); + cput(info); + cput(0); + WPUT(shndx); + symsize += ELF32SYMSIZE; break; } - - stroff = putelfstr(s); - LPUT(stroff); // string - cput((bind<<4)|(type&0xF)); - cput(0); - WPUT(shndx); - VPUT(addr); - VPUT(size); } void -asmelfsym64(void) +putelfsym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) { - genasmsym(putelfsym64); -} + int bind, type, shndx, off; -void -putelfsym32(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) -{ - int bind, type, shndx, stroff; - - bind = STB_GLOBAL; switch(t) { default: return; @@ -113,27 +99,27 @@ putelfsym32(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) break; case 'D': type = STT_OBJECT; - shndx = elftextsh + 1; + if((x->type&~SSUB) == SRODATA) + shndx = elftextsh + 1; + else + shndx = elftextsh + 2; break; case 'B': type = STT_OBJECT; - shndx = elftextsh + 2; + shndx = elftextsh + 3; break; } - - stroff = putelfstr(s); - LPUT(stroff); // string - LPUT(addr); - LPUT(size); - cput((bind<<4)|(type&0xF)); - cput(0); - WPUT(shndx); + bind = ver ? STB_LOCAL : STB_GLOBAL; + off = putelfstr(s); + putelfsyment(off, addr, size, (bind<<4)|(type&0xf), shndx); } void -asmelfsym32(void) +asmelfsym(void) { - genasmsym(putelfsym32); + // the first symbol entry is reserved + putelfsyment(0, 0, 0, (STB_LOCAL<<4)|STT_NOTYPE, 0); + genasmsym(putelfsym); } void diff --git a/src/libmach/8db.c b/src/libmach/8db.c index 80aa4fe69..5a195baf8 100644 --- a/src/libmach/8db.c +++ b/src/libmach/8db.c @@ -62,32 +62,32 @@ static char LESSSTACK[] = "sys·lessstack"; static char MORESTACK[] = "sys·morestack"; static char *excname[] = { -[0] "divide error", -[1] "debug exception", -[4] "overflow", -[5] "bounds check", -[6] "invalid opcode", -[7] "math coprocessor emulation", -[8] "double fault", -[9] "math coprocessor overrun", -[10] "invalid TSS", -[11] "segment not present", -[12] "stack exception", -[13] "general protection violation", -[14] "page fault", -[16] "math coprocessor error", -[17] "alignment check", -[18] "machine check", -[19] "floating-point exception", -[24] "clock", -[25] "keyboard", -[27] "modem status", -[28] "serial line status", -[30] "floppy disk", -[36] "mouse", -[37] "math coprocessor", -[38] "hard disk", -[64] "system call", +[0] = "divide error", +[1] = "debug exception", +[4] = "overflow", +[5] = "bounds check", +[6] = "invalid opcode", +[7] = "math coprocessor emulation", +[8] = "double fault", +[9] = "math coprocessor overrun", +[10] = "invalid TSS", +[11] = "segment not present", +[12] = "stack exception", +[13] = "general protection violation", +[14] = "page fault", +[16] = "math coprocessor error", +[17] = "alignment check", +[18] = "machine check", +[19] = "floating-point exception", +[24] = "clock", +[25] = "keyboard", +[27] = "modem status", +[28] = "serial line status", +[30] = "floppy disk", +[36] = "mouse", +[37] = "math coprocessor", +[38] = "hard disk", +[64] = "system call", }; Machdata i386mach = @@ -436,28 +436,28 @@ enum { static Optable optab0F00[8]= { -[0x00] 0,0, "MOVW LDT,%e", -[0x01] 0,0, "MOVW TR,%e", -[0x02] 0,0, "MOVW %e,LDT", -[0x03] 0,0, "MOVW %e,TR", -[0x04] 0,0, "VERR %e", -[0x05] 0,0, "VERW %e", +[0x00] = { 0,0, "MOVW LDT,%e" }, +[0x01] = { 0,0, "MOVW TR,%e" }, +[0x02] = { 0,0, "MOVW %e,LDT" }, +[0x03] = { 0,0, "MOVW %e,TR" }, +[0x04] = { 0,0, "VERR %e" }, +[0x05] = { 0,0, "VERW %e" }, }; static Optable optab0F01[8]= { -[0x00] 0,0, "MOVL GDTR,%e", -[0x01] 0,0, "MOVL IDTR,%e", -[0x02] 0,0, "MOVL %e,GDTR", -[0x03] 0,0, "MOVL %e,IDTR", -[0x04] 0,0, "MOVW MSW,%e", /* word */ -[0x06] 0,0, "MOVW %e,MSW", /* word */ -[0x07] 0,0, "INVLPG %e", /* or SWAPGS */ +[0x00] = { 0,0, "MOVL GDTR,%e" }, +[0x01] = { 0,0, "MOVL IDTR,%e" }, +[0x02] = { 0,0, "MOVL %e,GDTR" }, +[0x03] = { 0,0, "MOVL %e,IDTR" }, +[0x04] = { 0,0, "MOVW MSW,%e" }, /* word */ +[0x06] = { 0,0, "MOVW %e,MSW" }, /* word */ +[0x07] = { 0,0, "INVLPG %e" }, /* or SWAPGS */ }; static Optable optab0F01F8[1]= { -[0x00] 0,0, "SWAPGS", +[0x00] = { 0,0, "SWAPGS" }, }; /* 0F71 */ @@ -466,13 +466,13 @@ static Optable optab0F01F8[1]= static Optable optab0FAE[8]= { -[0x00] 0,0, "FXSAVE %e", -[0x01] 0,0, "FXRSTOR %e", -[0x02] 0,0, "LDMXCSR %e", -[0x03] 0,0, "STMXCSR %e", -[0x05] 0,0, "LFENCE", -[0x06] 0,0, "MFENCE", -[0x07] 0,0, "SFENCE", +[0x00] = { 0,0, "FXSAVE %e" }, +[0x01] = { 0,0, "FXRSTOR %e" }, +[0x02] = { 0,0, "LDMXCSR %e" }, +[0x03] = { 0,0, "STMXCSR %e" }, +[0x05] = { 0,0, "LFENCE" }, +[0x06] = { 0,0, "MFENCE" }, +[0x07] = { 0,0, "SFENCE" }, }; /* 0F18 */ @@ -480,455 +480,455 @@ static Optable optab0FAE[8]= static Optable optab0FBA[8]= { -[0x04] Ib,0, "BT%S %i,%e", -[0x05] Ib,0, "BTS%S %i,%e", -[0x06] Ib,0, "BTR%S %i,%e", -[0x07] Ib,0, "BTC%S %i,%e", +[0x04] = { Ib,0, "BT%S %i,%e" }, +[0x05] = { Ib,0, "BTS%S %i,%e" }, +[0x06] = { Ib,0, "BTR%S %i,%e" }, +[0x07] = { Ib,0, "BTC%S %i,%e" }, }; static Optable optab0F0F[256]= { -[0x0c] 0,0, "PI2FW %m,%M", -[0x0d] 0,0, "PI2L %m,%M", -[0x1c] 0,0, "PF2IW %m,%M", -[0x1d] 0,0, "PF2IL %m,%M", -[0x8a] 0,0, "PFNACC %m,%M", -[0x8e] 0,0, "PFPNACC %m,%M", -[0x90] 0,0, "PFCMPGE %m,%M", -[0x94] 0,0, "PFMIN %m,%M", -[0x96] 0,0, "PFRCP %m,%M", -[0x97] 0,0, "PFRSQRT %m,%M", -[0x9a] 0,0, "PFSUB %m,%M", -[0x9e] 0,0, "PFADD %m,%M", -[0xa0] 0,0, "PFCMPGT %m,%M", -[0xa4] 0,0, "PFMAX %m,%M", -[0xa6] 0,0, "PFRCPIT1 %m,%M", -[0xa7] 0,0, "PFRSQIT1 %m,%M", -[0xaa] 0,0, "PFSUBR %m,%M", -[0xae] 0,0, "PFACC %m,%M", -[0xb0] 0,0, "PFCMPEQ %m,%M", -[0xb4] 0,0, "PFMUL %m,%M", -[0xb6] 0,0, "PFRCPI2T %m,%M", -[0xb7] 0,0, "PMULHRW %m,%M", -[0xbb] 0,0, "PSWAPL %m,%M", +[0x0c] = { 0,0, "PI2FW %m,%M" }, +[0x0d] = { 0,0, "PI2L %m,%M" }, +[0x1c] = { 0,0, "PF2IW %m,%M" }, +[0x1d] = { 0,0, "PF2IL %m,%M" }, +[0x8a] = { 0,0, "PFNACC %m,%M" }, +[0x8e] = { 0,0, "PFPNACC %m,%M" }, +[0x90] = { 0,0, "PFCMPGE %m,%M" }, +[0x94] = { 0,0, "PFMIN %m,%M" }, +[0x96] = { 0,0, "PFRCP %m,%M" }, +[0x97] = { 0,0, "PFRSQRT %m,%M" }, +[0x9a] = { 0,0, "PFSUB %m,%M" }, +[0x9e] = { 0,0, "PFADD %m,%M" }, +[0xa0] = { 0,0, "PFCMPGT %m,%M" }, +[0xa4] = { 0,0, "PFMAX %m,%M" }, +[0xa6] = { 0,0, "PFRCPIT1 %m,%M" }, +[0xa7] = { 0,0, "PFRSQIT1 %m,%M" }, +[0xaa] = { 0,0, "PFSUBR %m,%M" }, +[0xae] = { 0,0, "PFACC %m,%M" }, +[0xb0] = { 0,0, "PFCMPEQ %m,%M" }, +[0xb4] = { 0,0, "PFMUL %m,%M" }, +[0xb6] = { 0,0, "PFRCPI2T %m,%M" }, +[0xb7] = { 0,0, "PMULHRW %m,%M" }, +[0xbb] = { 0,0, "PSWAPL %m,%M" }, }; static Optable optab0FC7[8]= { -[0x01] 0,0, "CMPXCHG8B %e", +[0x01] = { 0,0, "CMPXCHG8B %e" }, }; static Optable optab660F71[8]= { -[0x02] Ib,0, "PSRLW %i,%X", -[0x04] Ib,0, "PSRAW %i,%X", -[0x06] Ib,0, "PSLLW %i,%X", +[0x02] = { Ib,0, "PSRLW %i,%X" }, +[0x04] = { Ib,0, "PSRAW %i,%X" }, +[0x06] = { Ib,0, "PSLLW %i,%X" }, }; static Optable optab660F72[8]= { -[0x02] Ib,0, "PSRLL %i,%X", -[0x04] Ib,0, "PSRAL %i,%X", -[0x06] Ib,0, "PSLLL %i,%X", +[0x02] = { Ib,0, "PSRLL %i,%X" }, +[0x04] = { Ib,0, "PSRAL %i,%X" }, +[0x06] = { Ib,0, "PSLLL %i,%X" }, }; static Optable optab660F73[8]= { -[0x02] Ib,0, "PSRLQ %i,%X", -[0x03] Ib,0, "PSRLO %i,%X", -[0x06] Ib,0, "PSLLQ %i,%X", -[0x07] Ib,0, "PSLLO %i,%X", +[0x02] = { Ib,0, "PSRLQ %i,%X" }, +[0x03] = { Ib,0, "PSRLO %i,%X" }, +[0x06] = { Ib,0, "PSLLQ %i,%X" }, +[0x07] = { Ib,0, "PSLLO %i,%X" }, }; static Optable optab660F[256]= { -[0x2B] RM,0, "MOVNTPD %x,%e", -[0x2E] RM,0, "UCOMISD %x,%X", -[0x2F] RM,0, "COMISD %x,%X", -[0x5A] RM,0, "CVTPD2PS %x,%X", -[0x5B] RM,0, "CVTPS2PL %x,%X", -[0x6A] RM,0, "PUNPCKHLQ %x,%X", -[0x6B] RM,0, "PACKSSLW %x,%X", -[0x6C] RM,0, "PUNPCKLQDQ %x,%X", -[0x6D] RM,0, "PUNPCKHQDQ %x,%X", -[0x6E] RM,0, "MOV%S %e,%X", -[0x6F] RM,0, "MOVO %x,%X", /* MOVDQA */ -[0x70] RM,Ib, "PSHUFL %i,%x,%X", -[0x71] RMOP,0, optab660F71, -[0x72] RMOP,0, optab660F72, -[0x73] RMOP,0, optab660F73, -[0x7E] RM,0, "MOV%S %X,%e", -[0x7F] RM,0, "MOVO %X,%x", -[0xC4] RM,Ib, "PINSRW %i,%e,%X", -[0xC5] RMR,Ib, "PEXTRW %i,%X,%e", -[0xD4] RM,0, "PADDQ %x,%X", -[0xD5] RM,0, "PMULLW %x,%X", -[0xD6] RM,0, "MOVQ %X,%x", -[0xE6] RM,0, "CVTTPD2PL %x,%X", -[0xE7] RM,0, "MOVNTO %X,%e", -[0xF7] RM,0, "MASKMOVOU %x,%X", +[0x2B] = { RM,0, "MOVNTPD %x,%e" }, +[0x2E] = { RM,0, "UCOMISD %x,%X" }, +[0x2F] = { RM,0, "COMISD %x,%X" }, +[0x5A] = { RM,0, "CVTPD2PS %x,%X" }, +[0x5B] = { RM,0, "CVTPS2PL %x,%X" }, +[0x6A] = { RM,0, "PUNPCKHLQ %x,%X" }, +[0x6B] = { RM,0, "PACKSSLW %x,%X" }, +[0x6C] = { RM,0, "PUNPCKLQDQ %x,%X" }, +[0x6D] = { RM,0, "PUNPCKHQDQ %x,%X" }, +[0x6E] = { RM,0, "MOV%S %e,%X" }, +[0x6F] = { RM,0, "MOVO %x,%X" }, /* MOVDQA */ +[0x70] = { RM,Ib, "PSHUFL %i,%x,%X" }, +[0x71] = { RMOP,0, optab660F71 }, +[0x72] = { RMOP,0, optab660F72 }, +[0x73] = { RMOP,0, optab660F73 }, +[0x7E] = { RM,0, "MOV%S %X,%e" }, +[0x7F] = { RM,0, "MOVO %X,%x" }, +[0xC4] = { RM,Ib, "PINSRW %i,%e,%X" }, +[0xC5] = { RMR,Ib, "PEXTRW %i,%X,%e" }, +[0xD4] = { RM,0, "PADDQ %x,%X" }, +[0xD5] = { RM,0, "PMULLW %x,%X" }, +[0xD6] = { RM,0, "MOVQ %X,%x" }, +[0xE6] = { RM,0, "CVTTPD2PL %x,%X" }, +[0xE7] = { RM,0, "MOVNTO %X,%e" }, +[0xF7] = { RM,0, "MASKMOVOU %x,%X" }, }; static Optable optabF20F[256]= { -[0x10] RM,0, "MOVSD %x,%X", -[0x11] RM,0, "MOVSD %X,%x", -[0x2A] RM,0, "CVTS%S2SD %e,%X", -[0x2C] RM,0, "CVTTSD2S%S %x,%r", -[0x2D] RM,0, "CVTSD2S%S %x,%r", -[0x5A] RM,0, "CVTSD2SS %x,%X", -[0x6F] RM,0, "MOVOU %x,%X", -[0x70] RM,Ib, "PSHUFLW %i,%x,%X", -[0x7F] RM,0, "MOVOU %X,%x", -[0xD6] RM,0, "MOVQOZX %M,%X", -[0xE6] RM,0, "CVTPD2PL %x,%X", +[0x10] = { RM,0, "MOVSD %x,%X" }, +[0x11] = { RM,0, "MOVSD %X,%x" }, +[0x2A] = { RM,0, "CVTS%S2SD %e,%X" }, +[0x2C] = { RM,0, "CVTTSD2S%S %x,%r" }, +[0x2D] = { RM,0, "CVTSD2S%S %x,%r" }, +[0x5A] = { RM,0, "CVTSD2SS %x,%X" }, +[0x6F] = { RM,0, "MOVOU %x,%X" }, +[0x70] = { RM,Ib, "PSHUFLW %i,%x,%X" }, +[0x7F] = { RM,0, "MOVOU %X,%x" }, +[0xD6] = { RM,0, "MOVQOZX %M,%X" }, +[0xE6] = { RM,0, "CVTPD2PL %x,%X" }, }; static Optable optabF30F[256]= { -[0x10] RM,0, "MOVSS %x,%X", -[0x11] RM,0, "MOVSS %X,%x", -[0x2A] RM,0, "CVTS%S2SS %e,%X", -[0x2C] RM,0, "CVTTSS2S%S %x,%r", -[0x2D] RM,0, "CVTSS2S%S %x,%r", -[0x5A] RM,0, "CVTSS2SD %x,%X", -[0x5B] RM,0, "CVTTPS2PL %x,%X", -[0x6F] RM,0, "MOVOU %x,%X", -[0x70] RM,Ib, "PSHUFHW %i,%x,%X", -[0x7E] RM,0, "MOVQOZX %x,%X", -[0x7F] RM,0, "MOVOU %X,%x", -[0xD6] RM,0, "MOVQOZX %m*,%X", -[0xE6] RM,0, "CVTPL2PD %x,%X", +[0x10] = { RM,0, "MOVSS %x,%X" }, +[0x11] = { RM,0, "MOVSS %X,%x" }, +[0x2A] = { RM,0, "CVTS%S2SS %e,%X" }, +[0x2C] = { RM,0, "CVTTSS2S%S %x,%r" }, +[0x2D] = { RM,0, "CVTSS2S%S %x,%r" }, +[0x5A] = { RM,0, "CVTSS2SD %x,%X" }, +[0x5B] = { RM,0, "CVTTPS2PL %x,%X" }, +[0x6F] = { RM,0, "MOVOU %x,%X" }, +[0x70] = { RM,Ib, "PSHUFHW %i,%x,%X" }, +[0x7E] = { RM,0, "MOVQOZX %x,%X" }, +[0x7F] = { RM,0, "MOVOU %X,%x" }, +[0xD6] = { RM,0, "MOVQOZX %m*,%X" }, +[0xE6] = { RM,0, "CVTPL2PD %x,%X" }, }; static Optable optab0F[256]= { -[0x00] RMOP,0, optab0F00, -[0x01] RMOP,0, optab0F01, -[0x02] RM,0, "LAR %e,%r", -[0x03] RM,0, "LSL %e,%r", -[0x05] 0,0, "SYSCALL", -[0x06] 0,0, "CLTS", -[0x07] 0,0, "SYSRET", -[0x08] 0,0, "INVD", -[0x09] 0,0, "WBINVD", -[0x0B] 0,0, "UD2", -[0x0F] RM,AUX, optab0F0F, /* 3DNow! */ -[0x10] RM,0, "MOVU%s %x,%X", -[0x11] RM,0, "MOVU%s %X,%x", -[0x12] RM,0, "MOV[H]L%s %x,%X", /* TO DO: H if source is XMM */ -[0x13] RM,0, "MOVL%s %X,%e", -[0x14] RM,0, "UNPCKL%s %x,%X", -[0x15] RM,0, "UNPCKH%s %x,%X", -[0x16] RM,0, "MOV[L]H%s %x,%X", /* TO DO: L if source is XMM */ -[0x17] RM,0, "MOVH%s %X,%x", -[0x20] RMR,0, "MOVL %C,%e", -[0x21] RMR,0, "MOVL %D,%e", -[0x22] RMR,0, "MOVL %e,%C", -[0x23] RMR,0, "MOVL %e,%D", -[0x24] RMR,0, "MOVL %T,%e", -[0x26] RMR,0, "MOVL %e,%T", -[0x28] RM,0, "MOVA%s %x,%X", -[0x29] RM,0, "MOVA%s %X,%x", -[0x2A] RM,0, "CVTPL2%s %m*,%X", -[0x2B] RM,0, "MOVNT%s %X,%e", -[0x2C] RM,0, "CVTT%s2PL %x,%M", -[0x2D] RM,0, "CVT%s2PL %x,%M", -[0x2E] RM,0, "UCOMISS %x,%X", -[0x2F] RM,0, "COMISS %x,%X", -[0x30] 0,0, "WRMSR", -[0x31] 0,0, "RDTSC", -[0x32] 0,0, "RDMSR", -[0x33] 0,0, "RDPMC", -[0x42] RM,0, "CMOVC %e,%r", /* CF */ -[0x43] RM,0, "CMOVNC %e,%r", /* ¬ CF */ -[0x44] RM,0, "CMOVZ %e,%r", /* ZF */ -[0x45] RM,0, "CMOVNZ %e,%r", /* ¬ ZF */ -[0x46] RM,0, "CMOVBE %e,%r", /* CF ∨ ZF */ -[0x47] RM,0, "CMOVA %e,%r", /* ¬CF ∧ ¬ZF */ -[0x48] RM,0, "CMOVS %e,%r", /* SF */ -[0x49] RM,0, "CMOVNS %e,%r", /* ¬ SF */ -[0x4A] RM,0, "CMOVP %e,%r", /* PF */ -[0x4B] RM,0, "CMOVNP %e,%r", /* ¬ PF */ -[0x4C] RM,0, "CMOVLT %e,%r", /* LT ≡ OF ≠ SF */ -[0x4D] RM,0, "CMOVGE %e,%r", /* GE ≡ ZF ∨ SF */ -[0x4E] RM,0, "CMOVLE %e,%r", /* LE ≡ ZF ∨ LT */ -[0x4F] RM,0, "CMOVGT %e,%r", /* GT ≡ ¬ZF ∧ GE */ -[0x50] RM,0, "MOVMSK%s %X,%r", /* TO DO: check */ -[0x51] RM,0, "SQRT%s %x,%X", -[0x52] RM,0, "RSQRT%s %x,%X", -[0x53] RM,0, "RCP%s %x,%X", -[0x54] RM,0, "AND%s %x,%X", -[0x55] RM,0, "ANDN%s %x,%X", -[0x56] RM,0, "OR%s %x,%X", /* TO DO: S/D */ -[0x57] RM,0, "XOR%s %x,%X", /* S/D */ -[0x58] RM,0, "ADD%s %x,%X", /* S/P S/D */ -[0x59] RM,0, "MUL%s %x,%X", -[0x5A] RM,0, "CVTPS2PD %x,%X", -[0x5B] RM,0, "CVTPL2PS %x,%X", -[0x5C] RM,0, "SUB%s %x,%X", -[0x5D] RM,0, "MIN%s %x,%X", -[0x5E] RM,0, "DIV%s %x,%X", /* TO DO: S/P S/D */ -[0x5F] RM,0, "MAX%s %x,%X", -[0x60] RM,0, "PUNPCKLBW %m,%M", -[0x61] RM,0, "PUNPCKLWL %m,%M", -[0x62] RM,0, "PUNPCKLLQ %m,%M", -[0x63] RM,0, "PACKSSWB %m,%M", -[0x64] RM,0, "PCMPGTB %m,%M", -[0x65] RM,0, "PCMPGTW %m,%M", -[0x66] RM,0, "PCMPGTL %m,%M", -[0x67] RM,0, "PACKUSWB %m,%M", -[0x68] RM,0, "PUNPCKHBW %m,%M", -[0x69] RM,0, "PUNPCKHWL %m,%M", -[0x6A] RM,0, "PUNPCKHLQ %m,%M", -[0x6B] RM,0, "PACKSSLW %m,%M", -[0x6E] RM,0, "MOV%S %e,%M", -[0x6F] RM,0, "MOVQ %m,%M", -[0x70] RM,Ib, "PSHUFW %i,%m,%M", -[0x74] RM,0, "PCMPEQB %m,%M", -[0x75] RM,0, "PCMPEQW %m,%M", -[0x76] RM,0, "PCMPEQL %m,%M", -[0x7E] RM,0, "MOV%S %M,%e", -[0x7F] RM,0, "MOVQ %M,%m", -[0xAE] RMOP,0, optab0FAE, -[0xAA] 0,0, "RSM", -[0xB0] RM,0, "CMPXCHGB %r,%e", -[0xB1] RM,0, "CMPXCHG%S %r,%e", -[0xC0] RMB,0, "XADDB %r,%e", -[0xC1] RM,0, "XADD%S %r,%e", -[0xC2] RM,Ib, "CMP%s %x,%X,%#i", -[0xC3] RM,0, "MOVNTI%S %r,%e", -[0xC6] RM,Ib, "SHUF%s %i,%x,%X", -[0xC8] 0,0, "BSWAP AX", -[0xC9] 0,0, "BSWAP CX", -[0xCA] 0,0, "BSWAP DX", -[0xCB] 0,0, "BSWAP BX", -[0xCC] 0,0, "BSWAP SP", -[0xCD] 0,0, "BSWAP BP", -[0xCE] 0,0, "BSWAP SI", -[0xCF] 0,0, "BSWAP DI", -[0xD1] RM,0, "PSRLW %m,%M", -[0xD2] RM,0, "PSRLL %m,%M", -[0xD3] RM,0, "PSRLQ %m,%M", -[0xD5] RM,0, "PMULLW %m,%M", -[0xD6] RM,0, "MOVQOZX %m*,%X", -[0xD7] RM,0, "PMOVMSKB %m,%r", -[0xD8] RM,0, "PSUBUSB %m,%M", -[0xD9] RM,0, "PSUBUSW %m,%M", -[0xDA] RM,0, "PMINUB %m,%M", -[0xDB] RM,0, "PAND %m,%M", -[0xDC] RM,0, "PADDUSB %m,%M", -[0xDD] RM,0, "PADDUSW %m,%M", -[0xDE] RM,0, "PMAXUB %m,%M", -[0xDF] RM,0, "PANDN %m,%M", -[0xE0] RM,0, "PAVGB %m,%M", -[0xE1] RM,0, "PSRAW %m,%M", -[0xE2] RM,0, "PSRAL %m,%M", -[0xE3] RM,0, "PAVGW %m,%M", -[0xE4] RM,0, "PMULHUW %m,%M", -[0xE5] RM,0, "PMULHW %m,%M", -[0xE7] RM,0, "MOVNTQ %M,%e", -[0xE8] RM,0, "PSUBSB %m,%M", -[0xE9] RM,0, "PSUBSW %m,%M", -[0xEA] RM,0, "PMINSW %m,%M", -[0xEB] RM,0, "POR %m,%M", -[0xEC] RM,0, "PADDSB %m,%M", -[0xED] RM,0, "PADDSW %m,%M", -[0xEE] RM,0, "PMAXSW %m,%M", -[0xEF] RM,0, "PXOR %m,%M", -[0xF1] RM,0, "PSLLW %m,%M", -[0xF2] RM,0, "PSLLL %m,%M", -[0xF3] RM,0, "PSLLQ %m,%M", -[0xF4] RM,0, "PMULULQ %m,%M", -[0xF5] RM,0, "PMADDWL %m,%M", -[0xF6] RM,0, "PSADBW %m,%M", -[0xF7] RMR,0, "MASKMOVQ %m,%M", -[0xF8] RM,0, "PSUBB %m,%M", -[0xF9] RM,0, "PSUBW %m,%M", -[0xFA] RM,0, "PSUBL %m,%M", -[0xFC] RM,0, "PADDB %m,%M", -[0xFD] RM,0, "PADDW %m,%M", -[0xFE] RM,0, "PADDL %m,%M", - -[0x80] Iwds,0, "JOS %p", -[0x81] Iwds,0, "JOC %p", -[0x82] Iwds,0, "JCS %p", -[0x83] Iwds,0, "JCC %p", -[0x84] Iwds,0, "JEQ %p", -[0x85] Iwds,0, "JNE %p", -[0x86] Iwds,0, "JLS %p", -[0x87] Iwds,0, "JHI %p", -[0x88] Iwds,0, "JMI %p", -[0x89] Iwds,0, "JPL %p", -[0x8a] Iwds,0, "JPS %p", -[0x8b] Iwds,0, "JPC %p", -[0x8c] Iwds,0, "JLT %p", -[0x8d] Iwds,0, "JGE %p", -[0x8e] Iwds,0, "JLE %p", -[0x8f] Iwds,0, "JGT %p", -[0x90] RMB,0, "SETOS %e", -[0x91] RMB,0, "SETOC %e", -[0x92] RMB,0, "SETCS %e", -[0x93] RMB,0, "SETCC %e", -[0x94] RMB,0, "SETEQ %e", -[0x95] RMB,0, "SETNE %e", -[0x96] RMB,0, "SETLS %e", -[0x97] RMB,0, "SETHI %e", -[0x98] RMB,0, "SETMI %e", -[0x99] RMB,0, "SETPL %e", -[0x9a] RMB,0, "SETPS %e", -[0x9b] RMB,0, "SETPC %e", -[0x9c] RMB,0, "SETLT %e", -[0x9d] RMB,0, "SETGE %e", -[0x9e] RMB,0, "SETLE %e", -[0x9f] RMB,0, "SETGT %e", -[0xa0] 0,0, "PUSHL FS", -[0xa1] 0,0, "POPL FS", -[0xa2] 0,0, "CPUID", -[0xa3] RM,0, "BT%S %r,%e", -[0xa4] RM,Ib, "SHLD%S %r,%i,%e", -[0xa5] RM,0, "SHLD%S %r,CL,%e", -[0xa8] 0,0, "PUSHL GS", -[0xa9] 0,0, "POPL GS", -[0xab] RM,0, "BTS%S %r,%e", -[0xac] RM,Ib, "SHRD%S %r,%i,%e", -[0xad] RM,0, "SHRD%S %r,CL,%e", -[0xaf] RM,0, "IMUL%S %e,%r", -[0xb2] RMM,0, "LSS %e,%r", -[0xb3] RM,0, "BTR%S %r,%e", -[0xb4] RMM,0, "LFS %e,%r", -[0xb5] RMM,0, "LGS %e,%r", -[0xb6] RMB,0, "MOVBZX %e,%R", -[0xb7] RM,0, "MOVWZX %e,%R", -[0xba] RMOP,0, optab0FBA, -[0xbb] RM,0, "BTC%S %e,%r", -[0xbc] RM,0, "BSF%S %e,%r", -[0xbd] RM,0, "BSR%S %e,%r", -[0xbe] RMB,0, "MOVBSX %e,%R", -[0xbf] RM,0, "MOVWSX %e,%R", -[0xc7] RMOP,0, optab0FC7, +[0x00] = { RMOP,0, optab0F00 }, +[0x01] = { RMOP,0, optab0F01 }, +[0x02] = { RM,0, "LAR %e,%r" }, +[0x03] = { RM,0, "LSL %e,%r" }, +[0x05] = { 0,0, "SYSCALL" }, +[0x06] = { 0,0, "CLTS" }, +[0x07] = { 0,0, "SYSRET" }, +[0x08] = { 0,0, "INVD" }, +[0x09] = { 0,0, "WBINVD" }, +[0x0B] = { 0,0, "UD2" }, +[0x0F] = { RM,AUX, optab0F0F }, /* 3DNow! */ +[0x10] = { RM,0, "MOVU%s %x,%X" }, +[0x11] = { RM,0, "MOVU%s %X,%x" }, +[0x12] = { RM,0, "MOV[H]L%s %x,%X" }, /* TO DO: H if source is XMM */ +[0x13] = { RM,0, "MOVL%s %X,%e" }, +[0x14] = { RM,0, "UNPCKL%s %x,%X" }, +[0x15] = { RM,0, "UNPCKH%s %x,%X" }, +[0x16] = { RM,0, "MOV[L]H%s %x,%X" }, /* TO DO: L if source is XMM */ +[0x17] = { RM,0, "MOVH%s %X,%x" }, +[0x20] = { RMR,0, "MOVL %C,%e" }, +[0x21] = { RMR,0, "MOVL %D,%e" }, +[0x22] = { RMR,0, "MOVL %e,%C" }, +[0x23] = { RMR,0, "MOVL %e,%D" }, +[0x24] = { RMR,0, "MOVL %T,%e" }, +[0x26] = { RMR,0, "MOVL %e,%T" }, +[0x28] = { RM,0, "MOVA%s %x,%X" }, +[0x29] = { RM,0, "MOVA%s %X,%x" }, +[0x2A] = { RM,0, "CVTPL2%s %m*,%X" }, +[0x2B] = { RM,0, "MOVNT%s %X,%e" }, +[0x2C] = { RM,0, "CVTT%s2PL %x,%M" }, +[0x2D] = { RM,0, "CVT%s2PL %x,%M" }, +[0x2E] = { RM,0, "UCOMISS %x,%X" }, +[0x2F] = { RM,0, "COMISS %x,%X" }, +[0x30] = { 0,0, "WRMSR" }, +[0x31] = { 0,0, "RDTSC" }, +[0x32] = { 0,0, "RDMSR" }, +[0x33] = { 0,0, "RDPMC" }, +[0x42] = { RM,0, "CMOVC %e,%r" }, /* CF */ +[0x43] = { RM,0, "CMOVNC %e,%r" }, /* ¬ CF */ +[0x44] = { RM,0, "CMOVZ %e,%r" }, /* ZF */ +[0x45] = { RM,0, "CMOVNZ %e,%r" }, /* ¬ ZF */ +[0x46] = { RM,0, "CMOVBE %e,%r" }, /* CF ∨ ZF */ +[0x47] = { RM,0, "CMOVA %e,%r" }, /* ¬CF ∧ ¬ZF */ +[0x48] = { RM,0, "CMOVS %e,%r" }, /* SF */ +[0x49] = { RM,0, "CMOVNS %e,%r" }, /* ¬ SF */ +[0x4A] = { RM,0, "CMOVP %e,%r" }, /* PF */ +[0x4B] = { RM,0, "CMOVNP %e,%r" }, /* ¬ PF */ +[0x4C] = { RM,0, "CMOVLT %e,%r" }, /* LT ≡ OF ≠ SF */ +[0x4D] = { RM,0, "CMOVGE %e,%r" }, /* GE ≡ ZF ∨ SF */ +[0x4E] = { RM,0, "CMOVLE %e,%r" }, /* LE ≡ ZF ∨ LT */ +[0x4F] = { RM,0, "CMOVGT %e,%r" }, /* GT ≡ ¬ZF ∧ GE */ +[0x50] = { RM,0, "MOVMSK%s %X,%r" }, /* TO DO: check */ +[0x51] = { RM,0, "SQRT%s %x,%X" }, +[0x52] = { RM,0, "RSQRT%s %x,%X" }, +[0x53] = { RM,0, "RCP%s %x,%X" }, +[0x54] = { RM,0, "AND%s %x,%X" }, +[0x55] = { RM,0, "ANDN%s %x,%X" }, +[0x56] = { RM,0, "OR%s %x,%X" }, /* TO DO: S/D */ +[0x57] = { RM,0, "XOR%s %x,%X" }, /* S/D */ +[0x58] = { RM,0, "ADD%s %x,%X" }, /* S/P S/D */ +[0x59] = { RM,0, "MUL%s %x,%X" }, +[0x5A] = { RM,0, "CVTPS2PD %x,%X" }, +[0x5B] = { RM,0, "CVTPL2PS %x,%X" }, +[0x5C] = { RM,0, "SUB%s %x,%X" }, +[0x5D] = { RM,0, "MIN%s %x,%X" }, +[0x5E] = { RM,0, "DIV%s %x,%X" }, /* TO DO: S/P S/D */ +[0x5F] = { RM,0, "MAX%s %x,%X" }, +[0x60] = { RM,0, "PUNPCKLBW %m,%M" }, +[0x61] = { RM,0, "PUNPCKLWL %m,%M" }, +[0x62] = { RM,0, "PUNPCKLLQ %m,%M" }, +[0x63] = { RM,0, "PACKSSWB %m,%M" }, +[0x64] = { RM,0, "PCMPGTB %m,%M" }, +[0x65] = { RM,0, "PCMPGTW %m,%M" }, +[0x66] = { RM,0, "PCMPGTL %m,%M" }, +[0x67] = { RM,0, "PACKUSWB %m,%M" }, +[0x68] = { RM,0, "PUNPCKHBW %m,%M" }, +[0x69] = { RM,0, "PUNPCKHWL %m,%M" }, +[0x6A] = { RM,0, "PUNPCKHLQ %m,%M" }, +[0x6B] = { RM,0, "PACKSSLW %m,%M" }, +[0x6E] = { RM,0, "MOV%S %e,%M" }, +[0x6F] = { RM,0, "MOVQ %m,%M" }, +[0x70] = { RM,Ib, "PSHUFW %i,%m,%M" }, +[0x74] = { RM,0, "PCMPEQB %m,%M" }, +[0x75] = { RM,0, "PCMPEQW %m,%M" }, +[0x76] = { RM,0, "PCMPEQL %m,%M" }, +[0x7E] = { RM,0, "MOV%S %M,%e" }, +[0x7F] = { RM,0, "MOVQ %M,%m" }, +[0xAE] = { RMOP,0, optab0FAE }, +[0xAA] = { 0,0, "RSM" }, +[0xB0] = { RM,0, "CMPXCHGB %r,%e" }, +[0xB1] = { RM,0, "CMPXCHG%S %r,%e" }, +[0xC0] = { RMB,0, "XADDB %r,%e" }, +[0xC1] = { RM,0, "XADD%S %r,%e" }, +[0xC2] = { RM,Ib, "CMP%s %x,%X,%#i" }, +[0xC3] = { RM,0, "MOVNTI%S %r,%e" }, +[0xC6] = { RM,Ib, "SHUF%s %i,%x,%X" }, +[0xC8] = { 0,0, "BSWAP AX" }, +[0xC9] = { 0,0, "BSWAP CX" }, +[0xCA] = { 0,0, "BSWAP DX" }, +[0xCB] = { 0,0, "BSWAP BX" }, +[0xCC] = { 0,0, "BSWAP SP" }, +[0xCD] = { 0,0, "BSWAP BP" }, +[0xCE] = { 0,0, "BSWAP SI" }, +[0xCF] = { 0,0, "BSWAP DI" }, +[0xD1] = { RM,0, "PSRLW %m,%M" }, +[0xD2] = { RM,0, "PSRLL %m,%M" }, +[0xD3] = { RM,0, "PSRLQ %m,%M" }, +[0xD5] = { RM,0, "PMULLW %m,%M" }, +[0xD6] = { RM,0, "MOVQOZX %m*,%X" }, +[0xD7] = { RM,0, "PMOVMSKB %m,%r" }, +[0xD8] = { RM,0, "PSUBUSB %m,%M" }, +[0xD9] = { RM,0, "PSUBUSW %m,%M" }, +[0xDA] = { RM,0, "PMINUB %m,%M" }, +[0xDB] = { RM,0, "PAND %m,%M" }, +[0xDC] = { RM,0, "PADDUSB %m,%M" }, +[0xDD] = { RM,0, "PADDUSW %m,%M" }, +[0xDE] = { RM,0, "PMAXUB %m,%M" }, +[0xDF] = { RM,0, "PANDN %m,%M" }, +[0xE0] = { RM,0, "PAVGB %m,%M" }, +[0xE1] = { RM,0, "PSRAW %m,%M" }, +[0xE2] = { RM,0, "PSRAL %m,%M" }, +[0xE3] = { RM,0, "PAVGW %m,%M" }, +[0xE4] = { RM,0, "PMULHUW %m,%M" }, +[0xE5] = { RM,0, "PMULHW %m,%M" }, +[0xE7] = { RM,0, "MOVNTQ %M,%e" }, +[0xE8] = { RM,0, "PSUBSB %m,%M" }, +[0xE9] = { RM,0, "PSUBSW %m,%M" }, +[0xEA] = { RM,0, "PMINSW %m,%M" }, +[0xEB] = { RM,0, "POR %m,%M" }, +[0xEC] = { RM,0, "PADDSB %m,%M" }, +[0xED] = { RM,0, "PADDSW %m,%M" }, +[0xEE] = { RM,0, "PMAXSW %m,%M" }, +[0xEF] = { RM,0, "PXOR %m,%M" }, +[0xF1] = { RM,0, "PSLLW %m,%M" }, +[0xF2] = { RM,0, "PSLLL %m,%M" }, +[0xF3] = { RM,0, "PSLLQ %m,%M" }, +[0xF4] = { RM,0, "PMULULQ %m,%M" }, +[0xF5] = { RM,0, "PMADDWL %m,%M" }, +[0xF6] = { RM,0, "PSADBW %m,%M" }, +[0xF7] = { RMR,0, "MASKMOVQ %m,%M" }, +[0xF8] = { RM,0, "PSUBB %m,%M" }, +[0xF9] = { RM,0, "PSUBW %m,%M" }, +[0xFA] = { RM,0, "PSUBL %m,%M" }, +[0xFC] = { RM,0, "PADDB %m,%M" }, +[0xFD] = { RM,0, "PADDW %m,%M" }, +[0xFE] = { RM,0, "PADDL %m,%M" }, + +[0x80] = { Iwds,0, "JOS %p" }, +[0x81] = { Iwds,0, "JOC %p" }, +[0x82] = { Iwds,0, "JCS %p" }, +[0x83] = { Iwds,0, "JCC %p" }, +[0x84] = { Iwds,0, "JEQ %p" }, +[0x85] = { Iwds,0, "JNE %p" }, +[0x86] = { Iwds,0, "JLS %p" }, +[0x87] = { Iwds,0, "JHI %p" }, +[0x88] = { Iwds,0, "JMI %p" }, +[0x89] = { Iwds,0, "JPL %p" }, +[0x8a] = { Iwds,0, "JPS %p" }, +[0x8b] = { Iwds,0, "JPC %p" }, +[0x8c] = { Iwds,0, "JLT %p" }, +[0x8d] = { Iwds,0, "JGE %p" }, +[0x8e] = { Iwds,0, "JLE %p" }, +[0x8f] = { Iwds,0, "JGT %p" }, +[0x90] = { RMB,0, "SETOS %e" }, +[0x91] = { RMB,0, "SETOC %e" }, +[0x92] = { RMB,0, "SETCS %e" }, +[0x93] = { RMB,0, "SETCC %e" }, +[0x94] = { RMB,0, "SETEQ %e" }, +[0x95] = { RMB,0, "SETNE %e" }, +[0x96] = { RMB,0, "SETLS %e" }, +[0x97] = { RMB,0, "SETHI %e" }, +[0x98] = { RMB,0, "SETMI %e" }, +[0x99] = { RMB,0, "SETPL %e" }, +[0x9a] = { RMB,0, "SETPS %e" }, +[0x9b] = { RMB,0, "SETPC %e" }, +[0x9c] = { RMB,0, "SETLT %e" }, +[0x9d] = { RMB,0, "SETGE %e" }, +[0x9e] = { RMB,0, "SETLE %e" }, +[0x9f] = { RMB,0, "SETGT %e" }, +[0xa0] = { 0,0, "PUSHL FS" }, +[0xa1] = { 0,0, "POPL FS" }, +[0xa2] = { 0,0, "CPUID" }, +[0xa3] = { RM,0, "BT%S %r,%e" }, +[0xa4] = { RM,Ib, "SHLD%S %r,%i,%e" }, +[0xa5] = { RM,0, "SHLD%S %r,CL,%e" }, +[0xa8] = { 0,0, "PUSHL GS" }, +[0xa9] = { 0,0, "POPL GS" }, +[0xab] = { RM,0, "BTS%S %r,%e" }, +[0xac] = { RM,Ib, "SHRD%S %r,%i,%e" }, +[0xad] = { RM,0, "SHRD%S %r,CL,%e" }, +[0xaf] = { RM,0, "IMUL%S %e,%r" }, +[0xb2] = { RMM,0, "LSS %e,%r" }, +[0xb3] = { RM,0, "BTR%S %r,%e" }, +[0xb4] = { RMM,0, "LFS %e,%r" }, +[0xb5] = { RMM,0, "LGS %e,%r" }, +[0xb6] = { RMB,0, "MOVBZX %e,%R" }, +[0xb7] = { RM,0, "MOVWZX %e,%R" }, +[0xba] = { RMOP,0, optab0FBA }, +[0xbb] = { RM,0, "BTC%S %e,%r" }, +[0xbc] = { RM,0, "BSF%S %e,%r" }, +[0xbd] = { RM,0, "BSR%S %e,%r" }, +[0xbe] = { RMB,0, "MOVBSX %e,%R" }, +[0xbf] = { RM,0, "MOVWSX %e,%R" }, +[0xc7] = { RMOP,0, optab0FC7 }, }; static Optable optab80[8]= { -[0x00] Ib,0, "ADDB %i,%e", -[0x01] Ib,0, "ORB %i,%e", -[0x02] Ib,0, "ADCB %i,%e", -[0x03] Ib,0, "SBBB %i,%e", -[0x04] Ib,0, "ANDB %i,%e", -[0x05] Ib,0, "SUBB %i,%e", -[0x06] Ib,0, "XORB %i,%e", -[0x07] Ib,0, "CMPB %e,%i", +[0x00] = { Ib,0, "ADDB %i,%e" }, +[0x01] = { Ib,0, "ORB %i,%e" }, +[0x02] = { Ib,0, "ADCB %i,%e" }, +[0x03] = { Ib,0, "SBBB %i,%e" }, +[0x04] = { Ib,0, "ANDB %i,%e" }, +[0x05] = { Ib,0, "SUBB %i,%e" }, +[0x06] = { Ib,0, "XORB %i,%e" }, +[0x07] = { Ib,0, "CMPB %e,%i" }, }; static Optable optab81[8]= { -[0x00] Iwd,0, "ADD%S %i,%e", -[0x01] Iwd,0, "OR%S %i,%e", -[0x02] Iwd,0, "ADC%S %i,%e", -[0x03] Iwd,0, "SBB%S %i,%e", -[0x04] Iwd,0, "AND%S %i,%e", -[0x05] Iwd,0, "SUB%S %i,%e", -[0x06] Iwd,0, "XOR%S %i,%e", -[0x07] Iwd,0, "CMP%S %e,%i", +[0x00] = { Iwd,0, "ADD%S %i,%e" }, +[0x01] = { Iwd,0, "OR%S %i,%e" }, +[0x02] = { Iwd,0, "ADC%S %i,%e" }, +[0x03] = { Iwd,0, "SBB%S %i,%e" }, +[0x04] = { Iwd,0, "AND%S %i,%e" }, +[0x05] = { Iwd,0, "SUB%S %i,%e" }, +[0x06] = { Iwd,0, "XOR%S %i,%e" }, +[0x07] = { Iwd,0, "CMP%S %e,%i" }, }; static Optable optab83[8]= { -[0x00] Ibs,0, "ADD%S %i,%e", -[0x01] Ibs,0, "OR%S %i,%e", -[0x02] Ibs,0, "ADC%S %i,%e", -[0x03] Ibs,0, "SBB%S %i,%e", -[0x04] Ibs,0, "AND%S %i,%e", -[0x05] Ibs,0, "SUB%S %i,%e", -[0x06] Ibs,0, "XOR%S %i,%e", -[0x07] Ibs,0, "CMP%S %e,%i", +[0x00] = { Ibs,0, "ADD%S %i,%e" }, +[0x01] = { Ibs,0, "OR%S %i,%e" }, +[0x02] = { Ibs,0, "ADC%S %i,%e" }, +[0x03] = { Ibs,0, "SBB%S %i,%e" }, +[0x04] = { Ibs,0, "AND%S %i,%e" }, +[0x05] = { Ibs,0, "SUB%S %i,%e" }, +[0x06] = { Ibs,0, "XOR%S %i,%e" }, +[0x07] = { Ibs,0, "CMP%S %e,%i" }, }; static Optable optabC0[8] = { -[0x00] Ib,0, "ROLB %i,%e", -[0x01] Ib,0, "RORB %i,%e", -[0x02] Ib,0, "RCLB %i,%e", -[0x03] Ib,0, "RCRB %i,%e", -[0x04] Ib,0, "SHLB %i,%e", -[0x05] Ib,0, "SHRB %i,%e", -[0x07] Ib,0, "SARB %i,%e", +[0x00] = { Ib,0, "ROLB %i,%e" }, +[0x01] = { Ib,0, "RORB %i,%e" }, +[0x02] = { Ib,0, "RCLB %i,%e" }, +[0x03] = { Ib,0, "RCRB %i,%e" }, +[0x04] = { Ib,0, "SHLB %i,%e" }, +[0x05] = { Ib,0, "SHRB %i,%e" }, +[0x07] = { Ib,0, "SARB %i,%e" }, }; static Optable optabC1[8] = { -[0x00] Ib,0, "ROL%S %i,%e", -[0x01] Ib,0, "ROR%S %i,%e", -[0x02] Ib,0, "RCL%S %i,%e", -[0x03] Ib,0, "RCR%S %i,%e", -[0x04] Ib,0, "SHL%S %i,%e", -[0x05] Ib,0, "SHR%S %i,%e", -[0x07] Ib,0, "SAR%S %i,%e", +[0x00] = { Ib,0, "ROL%S %i,%e" }, +[0x01] = { Ib,0, "ROR%S %i,%e" }, +[0x02] = { Ib,0, "RCL%S %i,%e" }, +[0x03] = { Ib,0, "RCR%S %i,%e" }, +[0x04] = { Ib,0, "SHL%S %i,%e" }, +[0x05] = { Ib,0, "SHR%S %i,%e" }, +[0x07] = { Ib,0, "SAR%S %i,%e" }, }; static Optable optabD0[8] = { -[0x00] 0,0, "ROLB %e", -[0x01] 0,0, "RORB %e", -[0x02] 0,0, "RCLB %e", -[0x03] 0,0, "RCRB %e", -[0x04] 0,0, "SHLB %e", -[0x05] 0,0, "SHRB %e", -[0x07] 0,0, "SARB %e", +[0x00] = { 0,0, "ROLB %e" }, +[0x01] = { 0,0, "RORB %e" }, +[0x02] = { 0,0, "RCLB %e" }, +[0x03] = { 0,0, "RCRB %e" }, +[0x04] = { 0,0, "SHLB %e" }, +[0x05] = { 0,0, "SHRB %e" }, +[0x07] = { 0,0, "SARB %e" }, }; static Optable optabD1[8] = { -[0x00] 0,0, "ROL%S %e", -[0x01] 0,0, "ROR%S %e", -[0x02] 0,0, "RCL%S %e", -[0x03] 0,0, "RCR%S %e", -[0x04] 0,0, "SHL%S %e", -[0x05] 0,0, "SHR%S %e", -[0x07] 0,0, "SAR%S %e", +[0x00] = { 0,0, "ROL%S %e" }, +[0x01] = { 0,0, "ROR%S %e" }, +[0x02] = { 0,0, "RCL%S %e" }, +[0x03] = { 0,0, "RCR%S %e" }, +[0x04] = { 0,0, "SHL%S %e" }, +[0x05] = { 0,0, "SHR%S %e" }, +[0x07] = { 0,0, "SAR%S %e" }, }; static Optable optabD2[8] = { -[0x00] 0,0, "ROLB CL,%e", -[0x01] 0,0, "RORB CL,%e", -[0x02] 0,0, "RCLB CL,%e", -[0x03] 0,0, "RCRB CL,%e", -[0x04] 0,0, "SHLB CL,%e", -[0x05] 0,0, "SHRB CL,%e", -[0x07] 0,0, "SARB CL,%e", +[0x00] = { 0,0, "ROLB CL,%e" }, +[0x01] = { 0,0, "RORB CL,%e" }, +[0x02] = { 0,0, "RCLB CL,%e" }, +[0x03] = { 0,0, "RCRB CL,%e" }, +[0x04] = { 0,0, "SHLB CL,%e" }, +[0x05] = { 0,0, "SHRB CL,%e" }, +[0x07] = { 0,0, "SARB CL,%e" }, }; static Optable optabD3[8] = { -[0x00] 0,0, "ROL%S CL,%e", -[0x01] 0,0, "ROR%S CL,%e", -[0x02] 0,0, "RCL%S CL,%e", -[0x03] 0,0, "RCR%S CL,%e", -[0x04] 0,0, "SHL%S CL,%e", -[0x05] 0,0, "SHR%S CL,%e", -[0x07] 0,0, "SAR%S CL,%e", +[0x00] = { 0,0, "ROL%S CL,%e" }, +[0x01] = { 0,0, "ROR%S CL,%e" }, +[0x02] = { 0,0, "RCL%S CL,%e" }, +[0x03] = { 0,0, "RCR%S CL,%e" }, +[0x04] = { 0,0, "SHL%S CL,%e" }, +[0x05] = { 0,0, "SHR%S CL,%e" }, +[0x07] = { 0,0, "SAR%S CL,%e" }, }; static Optable optabD8[8+8] = { -[0x00] 0,0, "FADDF %e,F0", -[0x01] 0,0, "FMULF %e,F0", -[0x02] 0,0, "FCOMF %e,F0", -[0x03] 0,0, "FCOMFP %e,F0", -[0x04] 0,0, "FSUBF %e,F0", -[0x05] 0,0, "FSUBRF %e,F0", -[0x06] 0,0, "FDIVF %e,F0", -[0x07] 0,0, "FDIVRF %e,F0", -[0x08] 0,0, "FADDD %f,F0", -[0x09] 0,0, "FMULD %f,F0", -[0x0a] 0,0, "FCOMD %f,F0", -[0x0b] 0,0, "FCOMPD %f,F0", -[0x0c] 0,0, "FSUBD %f,F0", -[0x0d] 0,0, "FSUBRD %f,F0", -[0x0e] 0,0, "FDIVD %f,F0", -[0x0f] 0,0, "FDIVRD %f,F0", +[0x00] = { 0,0, "FADDF %e,F0" }, +[0x01] = { 0,0, "FMULF %e,F0" }, +[0x02] = { 0,0, "FCOMF %e,F0" }, +[0x03] = { 0,0, "FCOMFP %e,F0" }, +[0x04] = { 0,0, "FSUBF %e,F0" }, +[0x05] = { 0,0, "FSUBRF %e,F0" }, +[0x06] = { 0,0, "FDIVF %e,F0" }, +[0x07] = { 0,0, "FDIVRF %e,F0" }, +[0x08] = { 0,0, "FADDD %f,F0" }, +[0x09] = { 0,0, "FMULD %f,F0" }, +[0x0a] = { 0,0, "FCOMD %f,F0" }, +[0x0b] = { 0,0, "FCOMPD %f,F0" }, +[0x0c] = { 0,0, "FSUBD %f,F0" }, +[0x0d] = { 0,0, "FSUBRD %f,F0" }, +[0x0e] = { 0,0, "FDIVD %f,F0" }, +[0x0f] = { 0,0, "FDIVRD %f,F0" }, }; /* * optabD9 and optabDB use the following encoding: @@ -940,455 +940,455 @@ static Optable optabD8[8+8] = */ static Optable optabD9[64+8] = { -[0x00] 0,0, "FMOVF %e,F0", -[0x02] 0,0, "FMOVF F0,%e", -[0x03] 0,0, "FMOVFP F0,%e", -[0x04] 0,0, "FLDENV%S %e", -[0x05] 0,0, "FLDCW %e", -[0x06] 0,0, "FSTENV%S %e", -[0x07] 0,0, "FSTCW %e", -[0x08] 0,0, "FMOVD F0,F0", /* Mod R/M = 11xx xxxx*/ -[0x09] 0,0, "FMOVD F1,F0", -[0x0a] 0,0, "FMOVD F2,F0", -[0x0b] 0,0, "FMOVD F3,F0", -[0x0c] 0,0, "FMOVD F4,F0", -[0x0d] 0,0, "FMOVD F5,F0", -[0x0e] 0,0, "FMOVD F6,F0", -[0x0f] 0,0, "FMOVD F7,F0", -[0x10] 0,0, "FXCHD F0,F0", -[0x11] 0,0, "FXCHD F1,F0", -[0x12] 0,0, "FXCHD F2,F0", -[0x13] 0,0, "FXCHD F3,F0", -[0x14] 0,0, "FXCHD F4,F0", -[0x15] 0,0, "FXCHD F5,F0", -[0x16] 0,0, "FXCHD F6,F0", -[0x17] 0,0, "FXCHD F7,F0", -[0x18] 0,0, "FNOP", -[0x28] 0,0, "FCHS", -[0x29] 0,0, "FABS", -[0x2c] 0,0, "FTST", -[0x2d] 0,0, "FXAM", -[0x30] 0,0, "FLD1", -[0x31] 0,0, "FLDL2T", -[0x32] 0,0, "FLDL2E", -[0x33] 0,0, "FLDPI", -[0x34] 0,0, "FLDLG2", -[0x35] 0,0, "FLDLN2", -[0x36] 0,0, "FLDZ", -[0x38] 0,0, "F2XM1", -[0x39] 0,0, "FYL2X", -[0x3a] 0,0, "FPTAN", -[0x3b] 0,0, "FPATAN", -[0x3c] 0,0, "FXTRACT", -[0x3d] 0,0, "FPREM1", -[0x3e] 0,0, "FDECSTP", -[0x3f] 0,0, "FNCSTP", -[0x40] 0,0, "FPREM", -[0x41] 0,0, "FYL2XP1", -[0x42] 0,0, "FSQRT", -[0x43] 0,0, "FSINCOS", -[0x44] 0,0, "FRNDINT", -[0x45] 0,0, "FSCALE", -[0x46] 0,0, "FSIN", -[0x47] 0,0, "FCOS", +[0x00] = { 0,0, "FMOVF %e,F0" }, +[0x02] = { 0,0, "FMOVF F0,%e" }, +[0x03] = { 0,0, "FMOVFP F0,%e" }, +[0x04] = { 0,0, "FLDENV%S %e" }, +[0x05] = { 0,0, "FLDCW %e" }, +[0x06] = { 0,0, "FSTENV%S %e" }, +[0x07] = { 0,0, "FSTCW %e" }, +[0x08] = { 0,0, "FMOVD F0,F0" }, /* Mod R/M = 11xx xxxx*/ +[0x09] = { 0,0, "FMOVD F1,F0" }, +[0x0a] = { 0,0, "FMOVD F2,F0" }, +[0x0b] = { 0,0, "FMOVD F3,F0" }, +[0x0c] = { 0,0, "FMOVD F4,F0" }, +[0x0d] = { 0,0, "FMOVD F5,F0" }, +[0x0e] = { 0,0, "FMOVD F6,F0" }, +[0x0f] = { 0,0, "FMOVD F7,F0" }, +[0x10] = { 0,0, "FXCHD F0,F0" }, +[0x11] = { 0,0, "FXCHD F1,F0" }, +[0x12] = { 0,0, "FXCHD F2,F0" }, +[0x13] = { 0,0, "FXCHD F3,F0" }, +[0x14] = { 0,0, "FXCHD F4,F0" }, +[0x15] = { 0,0, "FXCHD F5,F0" }, +[0x16] = { 0,0, "FXCHD F6,F0" }, +[0x17] = { 0,0, "FXCHD F7,F0" }, +[0x18] = { 0,0, "FNOP" }, +[0x28] = { 0,0, "FCHS" }, +[0x29] = { 0,0, "FABS" }, +[0x2c] = { 0,0, "FTST" }, +[0x2d] = { 0,0, "FXAM" }, +[0x30] = { 0,0, "FLD1" }, +[0x31] = { 0,0, "FLDL2T" }, +[0x32] = { 0,0, "FLDL2E" }, +[0x33] = { 0,0, "FLDPI" }, +[0x34] = { 0,0, "FLDLG2" }, +[0x35] = { 0,0, "FLDLN2" }, +[0x36] = { 0,0, "FLDZ" }, +[0x38] = { 0,0, "F2XM1" }, +[0x39] = { 0,0, "FYL2X" }, +[0x3a] = { 0,0, "FPTAN" }, +[0x3b] = { 0,0, "FPATAN" }, +[0x3c] = { 0,0, "FXTRACT" }, +[0x3d] = { 0,0, "FPREM1" }, +[0x3e] = { 0,0, "FDECSTP" }, +[0x3f] = { 0,0, "FNCSTP" }, +[0x40] = { 0,0, "FPREM" }, +[0x41] = { 0,0, "FYL2XP1" }, +[0x42] = { 0,0, "FSQRT" }, +[0x43] = { 0,0, "FSINCOS" }, +[0x44] = { 0,0, "FRNDINT" }, +[0x45] = { 0,0, "FSCALE" }, +[0x46] = { 0,0, "FSIN" }, +[0x47] = { 0,0, "FCOS" }, }; static Optable optabDA[8+8] = { -[0x00] 0,0, "FADDL %e,F0", -[0x01] 0,0, "FMULL %e,F0", -[0x02] 0,0, "FCOML %e,F0", -[0x03] 0,0, "FCOMLP %e,F0", -[0x04] 0,0, "FSUBL %e,F0", -[0x05] 0,0, "FSUBRL %e,F0", -[0x06] 0,0, "FDIVL %e,F0", -[0x07] 0,0, "FDIVRL %e,F0", -[0x08] 0,0, "FCMOVCS %f,F0", -[0x09] 0,0, "FCMOVEQ %f,F0", -[0x0a] 0,0, "FCMOVLS %f,F0", -[0x0b] 0,0, "FCMOVUN %f,F0", -[0x0d] Op_R1,0, "FUCOMPP", +[0x00] = { 0,0, "FADDL %e,F0" }, +[0x01] = { 0,0, "FMULL %e,F0" }, +[0x02] = { 0,0, "FCOML %e,F0" }, +[0x03] = { 0,0, "FCOMLP %e,F0" }, +[0x04] = { 0,0, "FSUBL %e,F0" }, +[0x05] = { 0,0, "FSUBRL %e,F0" }, +[0x06] = { 0,0, "FDIVL %e,F0" }, +[0x07] = { 0,0, "FDIVRL %e,F0" }, +[0x08] = { 0,0, "FCMOVCS %f,F0" }, +[0x09] = { 0,0, "FCMOVEQ %f,F0" }, +[0x0a] = { 0,0, "FCMOVLS %f,F0" }, +[0x0b] = { 0,0, "FCMOVUN %f,F0" }, +[0x0d] = { Op_R1,0, "FUCOMPP" }, }; static Optable optabDB[8+64] = { -[0x00] 0,0, "FMOVL %e,F0", -[0x02] 0,0, "FMOVL F0,%e", -[0x03] 0,0, "FMOVLP F0,%e", -[0x05] 0,0, "FMOVX %e,F0", -[0x07] 0,0, "FMOVXP F0,%e", -[0x08] 0,0, "FCMOVCC %f,F0", -[0x09] 0,0, "FCMOVNE %f,F0", -[0x0a] 0,0, "FCMOVHI %f,F0", -[0x0b] 0,0, "FCMOVNU %f,F0", -[0x0d] 0,0, "FUCOMI F0,%f", -[0x0e] 0,0, "FCOMI F0,%f", -[0x2a] 0,0, "FCLEX", -[0x2b] 0,0, "FINIT", +[0x00] = { 0,0, "FMOVL %e,F0" }, +[0x02] = { 0,0, "FMOVL F0,%e" }, +[0x03] = { 0,0, "FMOVLP F0,%e" }, +[0x05] = { 0,0, "FMOVX %e,F0" }, +[0x07] = { 0,0, "FMOVXP F0,%e" }, +[0x08] = { 0,0, "FCMOVCC %f,F0" }, +[0x09] = { 0,0, "FCMOVNE %f,F0" }, +[0x0a] = { 0,0, "FCMOVHI %f,F0" }, +[0x0b] = { 0,0, "FCMOVNU %f,F0" }, +[0x0d] = { 0,0, "FUCOMI F0,%f" }, +[0x0e] = { 0,0, "FCOMI F0,%f" }, +[0x2a] = { 0,0, "FCLEX" }, +[0x2b] = { 0,0, "FINIT" }, }; static Optable optabDC[8+8] = { -[0x00] 0,0, "FADDD %e,F0", -[0x01] 0,0, "FMULD %e,F0", -[0x02] 0,0, "FCOMD %e,F0", -[0x03] 0,0, "FCOMDP %e,F0", -[0x04] 0,0, "FSUBD %e,F0", -[0x05] 0,0, "FSUBRD %e,F0", -[0x06] 0,0, "FDIVD %e,F0", -[0x07] 0,0, "FDIVRD %e,F0", -[0x08] 0,0, "FADDD F0,%f", -[0x09] 0,0, "FMULD F0,%f", -[0x0c] 0,0, "FSUBRD F0,%f", -[0x0d] 0,0, "FSUBD F0,%f", -[0x0e] 0,0, "FDIVRD F0,%f", -[0x0f] 0,0, "FDIVD F0,%f", +[0x00] = { 0,0, "FADDD %e,F0" }, +[0x01] = { 0,0, "FMULD %e,F0" }, +[0x02] = { 0,0, "FCOMD %e,F0" }, +[0x03] = { 0,0, "FCOMDP %e,F0" }, +[0x04] = { 0,0, "FSUBD %e,F0" }, +[0x05] = { 0,0, "FSUBRD %e,F0" }, +[0x06] = { 0,0, "FDIVD %e,F0" }, +[0x07] = { 0,0, "FDIVRD %e,F0" }, +[0x08] = { 0,0, "FADDD F0,%f" }, +[0x09] = { 0,0, "FMULD F0,%f" }, +[0x0c] = { 0,0, "FSUBRD F0,%f" }, +[0x0d] = { 0,0, "FSUBD F0,%f" }, +[0x0e] = { 0,0, "FDIVRD F0,%f" }, +[0x0f] = { 0,0, "FDIVD F0,%f" }, }; static Optable optabDD[8+8] = { -[0x00] 0,0, "FMOVD %e,F0", -[0x02] 0,0, "FMOVD F0,%e", -[0x03] 0,0, "FMOVDP F0,%e", -[0x04] 0,0, "FRSTOR%S %e", -[0x06] 0,0, "FSAVE%S %e", -[0x07] 0,0, "FSTSW %e", -[0x08] 0,0, "FFREED %f", -[0x0a] 0,0, "FMOVD %f,F0", -[0x0b] 0,0, "FMOVDP %f,F0", -[0x0c] 0,0, "FUCOMD %f,F0", -[0x0d] 0,0, "FUCOMDP %f,F0", +[0x00] = { 0,0, "FMOVD %e,F0" }, +[0x02] = { 0,0, "FMOVD F0,%e" }, +[0x03] = { 0,0, "FMOVDP F0,%e" }, +[0x04] = { 0,0, "FRSTOR%S %e" }, +[0x06] = { 0,0, "FSAVE%S %e" }, +[0x07] = { 0,0, "FSTSW %e" }, +[0x08] = { 0,0, "FFREED %f" }, +[0x0a] = { 0,0, "FMOVD %f,F0" }, +[0x0b] = { 0,0, "FMOVDP %f,F0" }, +[0x0c] = { 0,0, "FUCOMD %f,F0" }, +[0x0d] = { 0,0, "FUCOMDP %f,F0" }, }; static Optable optabDE[8+8] = { -[0x00] 0,0, "FADDW %e,F0", -[0x01] 0,0, "FMULW %e,F0", -[0x02] 0,0, "FCOMW %e,F0", -[0x03] 0,0, "FCOMWP %e,F0", -[0x04] 0,0, "FSUBW %e,F0", -[0x05] 0,0, "FSUBRW %e,F0", -[0x06] 0,0, "FDIVW %e,F0", -[0x07] 0,0, "FDIVRW %e,F0", -[0x08] 0,0, "FADDDP F0,%f", -[0x09] 0,0, "FMULDP F0,%f", -[0x0b] Op_R1,0, "FCOMPDP", -[0x0c] 0,0, "FSUBRDP F0,%f", -[0x0d] 0,0, "FSUBDP F0,%f", -[0x0e] 0,0, "FDIVRDP F0,%f", -[0x0f] 0,0, "FDIVDP F0,%f", +[0x00] = { 0,0, "FADDW %e,F0" }, +[0x01] = { 0,0, "FMULW %e,F0" }, +[0x02] = { 0,0, "FCOMW %e,F0" }, +[0x03] = { 0,0, "FCOMWP %e,F0" }, +[0x04] = { 0,0, "FSUBW %e,F0" }, +[0x05] = { 0,0, "FSUBRW %e,F0" }, +[0x06] = { 0,0, "FDIVW %e,F0" }, +[0x07] = { 0,0, "FDIVRW %e,F0" }, +[0x08] = { 0,0, "FADDDP F0,%f" }, +[0x09] = { 0,0, "FMULDP F0,%f" }, +[0x0b] = { Op_R1,0, "FCOMPDP" }, +[0x0c] = { 0,0, "FSUBRDP F0,%f" }, +[0x0d] = { 0,0, "FSUBDP F0,%f" }, +[0x0e] = { 0,0, "FDIVRDP F0,%f" }, +[0x0f] = { 0,0, "FDIVDP F0,%f" }, }; static Optable optabDF[8+8] = { -[0x00] 0,0, "FMOVW %e,F0", -[0x02] 0,0, "FMOVW F0,%e", -[0x03] 0,0, "FMOVWP F0,%e", -[0x04] 0,0, "FBLD %e", -[0x05] 0,0, "FMOVL %e,F0", -[0x06] 0,0, "FBSTP %e", -[0x07] 0,0, "FMOVLP F0,%e", -[0x0c] Op_R0,0, "FSTSW %OAX", -[0x0d] 0,0, "FUCOMIP F0,%f", -[0x0e] 0,0, "FCOMIP F0,%f", +[0x00] = { 0,0, "FMOVW %e,F0" }, +[0x02] = { 0,0, "FMOVW F0,%e" }, +[0x03] = { 0,0, "FMOVWP F0,%e" }, +[0x04] = { 0,0, "FBLD %e" }, +[0x05] = { 0,0, "FMOVL %e,F0" }, +[0x06] = { 0,0, "FBSTP %e" }, +[0x07] = { 0,0, "FMOVLP F0,%e" }, +[0x0c] = { Op_R0,0, "FSTSW %OAX" }, +[0x0d] = { 0,0, "FUCOMIP F0,%f" }, +[0x0e] = { 0,0, "FCOMIP F0,%f" }, }; static Optable optabF6[8] = { -[0x00] Ib,0, "TESTB %i,%e", -[0x02] 0,0, "NOTB %e", -[0x03] 0,0, "NEGB %e", -[0x04] 0,0, "MULB AL,%e", -[0x05] 0,0, "IMULB AL,%e", -[0x06] 0,0, "DIVB AL,%e", -[0x07] 0,0, "IDIVB AL,%e", +[0x00] = { Ib,0, "TESTB %i,%e" }, +[0x02] = { 0,0, "NOTB %e" }, +[0x03] = { 0,0, "NEGB %e" }, +[0x04] = { 0,0, "MULB AL,%e" }, +[0x05] = { 0,0, "IMULB AL,%e" }, +[0x06] = { 0,0, "DIVB AL,%e" }, +[0x07] = { 0,0, "IDIVB AL,%e" }, }; static Optable optabF7[8] = { -[0x00] Iwd,0, "TEST%S %i,%e", -[0x02] 0,0, "NOT%S %e", -[0x03] 0,0, "NEG%S %e", -[0x04] 0,0, "MUL%S %OAX,%e", -[0x05] 0,0, "IMUL%S %OAX,%e", -[0x06] 0,0, "DIV%S %OAX,%e", -[0x07] 0,0, "IDIV%S %OAX,%e", +[0x00] = { Iwd,0, "TEST%S %i,%e" }, +[0x02] = { 0,0, "NOT%S %e" }, +[0x03] = { 0,0, "NEG%S %e" }, +[0x04] = { 0,0, "MUL%S %OAX,%e" }, +[0x05] = { 0,0, "IMUL%S %OAX,%e" }, +[0x06] = { 0,0, "DIV%S %OAX,%e" }, +[0x07] = { 0,0, "IDIV%S %OAX,%e" }, }; static Optable optabFE[8] = { -[0x00] 0,0, "INCB %e", -[0x01] 0,0, "DECB %e", +[0x00] = { 0,0, "INCB %e" }, +[0x01] = { 0,0, "DECB %e" }, }; static Optable optabFF[8] = { -[0x00] 0,0, "INC%S %e", -[0x01] 0,0, "DEC%S %e", -[0x02] JUMP,0, "CALL* %e", -[0x03] JUMP,0, "CALLF* %e", -[0x04] JUMP,0, "JMP* %e", -[0x05] JUMP,0, "JMPF* %e", -[0x06] 0,0, "PUSHL %e", +[0x00] = { 0,0, "INC%S %e" }, +[0x01] = { 0,0, "DEC%S %e" }, +[0x02] = { JUMP,0, "CALL* %e" }, +[0x03] = { JUMP,0, "CALLF* %e" }, +[0x04] = { JUMP,0, "JMP* %e" }, +[0x05] = { JUMP,0, "JMPF* %e" }, +[0x06] = { 0,0, "PUSHL %e" }, }; static Optable optable[256+2] = { -[0x00] RMB,0, "ADDB %r,%e", -[0x01] RM,0, "ADD%S %r,%e", -[0x02] RMB,0, "ADDB %e,%r", -[0x03] RM,0, "ADD%S %e,%r", -[0x04] Ib,0, "ADDB %i,AL", -[0x05] Iwd,0, "ADD%S %i,%OAX", -[0x06] 0,0, "PUSHL ES", -[0x07] 0,0, "POPL ES", -[0x08] RMB,0, "ORB %r,%e", -[0x09] RM,0, "OR%S %r,%e", -[0x0a] RMB,0, "ORB %e,%r", -[0x0b] RM,0, "OR%S %e,%r", -[0x0c] Ib,0, "ORB %i,AL", -[0x0d] Iwd,0, "OR%S %i,%OAX", -[0x0e] 0,0, "PUSHL CS", -[0x0f] AUXMM,0, optab0F, -[0x10] RMB,0, "ADCB %r,%e", -[0x11] RM,0, "ADC%S %r,%e", -[0x12] RMB,0, "ADCB %e,%r", -[0x13] RM,0, "ADC%S %e,%r", -[0x14] Ib,0, "ADCB %i,AL", -[0x15] Iwd,0, "ADC%S %i,%OAX", -[0x16] 0,0, "PUSHL SS", -[0x17] 0,0, "POPL SS", -[0x18] RMB,0, "SBBB %r,%e", -[0x19] RM,0, "SBB%S %r,%e", -[0x1a] RMB,0, "SBBB %e,%r", -[0x1b] RM,0, "SBB%S %e,%r", -[0x1c] Ib,0, "SBBB %i,AL", -[0x1d] Iwd,0, "SBB%S %i,%OAX", -[0x1e] 0,0, "PUSHL DS", -[0x1f] 0,0, "POPL DS", -[0x20] RMB,0, "ANDB %r,%e", -[0x21] RM,0, "AND%S %r,%e", -[0x22] RMB,0, "ANDB %e,%r", -[0x23] RM,0, "AND%S %e,%r", -[0x24] Ib,0, "ANDB %i,AL", -[0x25] Iwd,0, "AND%S %i,%OAX", -[0x26] SEG,0, "ES:", -[0x27] 0,0, "DAA", -[0x28] RMB,0, "SUBB %r,%e", -[0x29] RM,0, "SUB%S %r,%e", -[0x2a] RMB,0, "SUBB %e,%r", -[0x2b] RM,0, "SUB%S %e,%r", -[0x2c] Ib,0, "SUBB %i,AL", -[0x2d] Iwd,0, "SUB%S %i,%OAX", -[0x2e] SEG,0, "CS:", -[0x2f] 0,0, "DAS", -[0x30] RMB,0, "XORB %r,%e", -[0x31] RM,0, "XOR%S %r,%e", -[0x32] RMB,0, "XORB %e,%r", -[0x33] RM,0, "XOR%S %e,%r", -[0x34] Ib,0, "XORB %i,AL", -[0x35] Iwd,0, "XOR%S %i,%OAX", -[0x36] SEG,0, "SS:", -[0x37] 0,0, "AAA", -[0x38] RMB,0, "CMPB %r,%e", -[0x39] RM,0, "CMP%S %r,%e", -[0x3a] RMB,0, "CMPB %e,%r", -[0x3b] RM,0, "CMP%S %e,%r", -[0x3c] Ib,0, "CMPB %i,AL", -[0x3d] Iwd,0, "CMP%S %i,%OAX", -[0x3e] SEG,0, "DS:", -[0x3f] 0,0, "AAS", -[0x40] 0,0, "INC%S %OAX", -[0x41] 0,0, "INC%S %OCX", -[0x42] 0,0, "INC%S %ODX", -[0x43] 0,0, "INC%S %OBX", -[0x44] 0,0, "INC%S %OSP", -[0x45] 0,0, "INC%S %OBP", -[0x46] 0,0, "INC%S %OSI", -[0x47] 0,0, "INC%S %ODI", -[0x48] 0,0, "DEC%S %OAX", -[0x49] 0,0, "DEC%S %OCX", -[0x4a] 0,0, "DEC%S %ODX", -[0x4b] 0,0, "DEC%S %OBX", -[0x4c] 0,0, "DEC%S %OSP", -[0x4d] 0,0, "DEC%S %OBP", -[0x4e] 0,0, "DEC%S %OSI", -[0x4f] 0,0, "DEC%S %ODI", -[0x50] 0,0, "PUSH%S %OAX", -[0x51] 0,0, "PUSH%S %OCX", -[0x52] 0,0, "PUSH%S %ODX", -[0x53] 0,0, "PUSH%S %OBX", -[0x54] 0,0, "PUSH%S %OSP", -[0x55] 0,0, "PUSH%S %OBP", -[0x56] 0,0, "PUSH%S %OSI", -[0x57] 0,0, "PUSH%S %ODI", -[0x58] 0,0, "POP%S %OAX", -[0x59] 0,0, "POP%S %OCX", -[0x5a] 0,0, "POP%S %ODX", -[0x5b] 0,0, "POP%S %OBX", -[0x5c] 0,0, "POP%S %OSP", -[0x5d] 0,0, "POP%S %OBP", -[0x5e] 0,0, "POP%S %OSI", -[0x5f] 0,0, "POP%S %ODI", -[0x60] 0,0, "PUSHA%S", -[0x61] 0,0, "POPA%S", -[0x62] RMM,0, "BOUND %e,%r", -[0x63] RM,0, "ARPL %r,%e", -[0x64] SEG,0, "FS:", -[0x65] SEG,0, "GS:", -[0x66] OPOVER,0, "", -[0x67] ADDOVER,0, "", -[0x68] Iwd,0, "PUSH%S %i", -[0x69] RM,Iwd, "IMUL%S %e,%i,%r", -[0x6a] Ib,0, "PUSH%S %i", -[0x6b] RM,Ibs, "IMUL%S %e,%i,%r", -[0x6c] 0,0, "INSB DX,(%ODI)", -[0x6d] 0,0, "INS%S DX,(%ODI)", -[0x6e] 0,0, "OUTSB (%ASI),DX", -[0x6f] 0,0, "OUTS%S (%ASI),DX", -[0x70] Jbs,0, "JOS %p", -[0x71] Jbs,0, "JOC %p", -[0x72] Jbs,0, "JCS %p", -[0x73] Jbs,0, "JCC %p", -[0x74] Jbs,0, "JEQ %p", -[0x75] Jbs,0, "JNE %p", -[0x76] Jbs,0, "JLS %p", -[0x77] Jbs,0, "JHI %p", -[0x78] Jbs,0, "JMI %p", -[0x79] Jbs,0, "JPL %p", -[0x7a] Jbs,0, "JPS %p", -[0x7b] Jbs,0, "JPC %p", -[0x7c] Jbs,0, "JLT %p", -[0x7d] Jbs,0, "JGE %p", -[0x7e] Jbs,0, "JLE %p", -[0x7f] Jbs,0, "JGT %p", -[0x80] RMOPB,0, optab80, -[0x81] RMOP,0, optab81, -[0x83] RMOP,0, optab83, -[0x84] RMB,0, "TESTB %r,%e", -[0x85] RM,0, "TEST%S %r,%e", -[0x86] RMB,0, "XCHGB %r,%e", -[0x87] RM,0, "XCHG%S %r,%e", -[0x88] RMB,0, "MOVB %r,%e", -[0x89] RM,0, "MOV%S %r,%e", -[0x8a] RMB,0, "MOVB %e,%r", -[0x8b] RM,0, "MOV%S %e,%r", -[0x8c] RM,0, "MOVW %g,%e", -[0x8d] RM,0, "LEA%S %e,%r", -[0x8e] RM,0, "MOVW %e,%g", -[0x8f] RM,0, "POP%S %e", -[0x90] 0,0, "NOP", -[0x91] 0,0, "XCHG %OCX,%OAX", -[0x92] 0,0, "XCHG %ODX,%OAX", -[0x93] 0,0, "XCHG %OBX,%OAX", -[0x94] 0,0, "XCHG %OSP,%OAX", -[0x95] 0,0, "XCHG %OBP,%OAX", -[0x96] 0,0, "XCHG %OSI,%OAX", -[0x97] 0,0, "XCHG %ODI,%OAX", -[0x98] 0,0, "%W", /* miserable CBW or CWDE */ -[0x99] 0,0, "%w", /* idiotic CWD or CDQ */ -[0x9a] PTR,0, "CALL%S %d", -[0x9b] 0,0, "WAIT", -[0x9c] 0,0, "PUSHF", -[0x9d] 0,0, "POPF", -[0x9e] 0,0, "SAHF", -[0x9f] 0,0, "LAHF", -[0xa0] Awd,0, "MOVB %i,AL", -[0xa1] Awd,0, "MOV%S %i,%OAX", -[0xa2] Awd,0, "MOVB AL,%i", -[0xa3] Awd,0, "MOV%S %OAX,%i", -[0xa4] 0,0, "MOVSB (%ASI),(%ADI)", -[0xa5] 0,0, "MOVS%S (%ASI),(%ADI)", -[0xa6] 0,0, "CMPSB (%ASI),(%ADI)", -[0xa7] 0,0, "CMPS%S (%ASI),(%ADI)", -[0xa8] Ib,0, "TESTB %i,AL", -[0xa9] Iwd,0, "TEST%S %i,%OAX", -[0xaa] 0,0, "STOSB AL,(%ADI)", -[0xab] 0,0, "STOS%S %OAX,(%ADI)", -[0xac] 0,0, "LODSB (%ASI),AL", -[0xad] 0,0, "LODS%S (%ASI),%OAX", -[0xae] 0,0, "SCASB (%ADI),AL", -[0xaf] 0,0, "SCAS%S (%ADI),%OAX", -[0xb0] Ib,0, "MOVB %i,AL", -[0xb1] Ib,0, "MOVB %i,CL", -[0xb2] Ib,0, "MOVB %i,DL", -[0xb3] Ib,0, "MOVB %i,BL", -[0xb4] Ib,0, "MOVB %i,AH", -[0xb5] Ib,0, "MOVB %i,CH", -[0xb6] Ib,0, "MOVB %i,DH", -[0xb7] Ib,0, "MOVB %i,BH", -[0xb8] Iwdq,0, "MOV%S %i,%OAX", -[0xb9] Iwdq,0, "MOV%S %i,%OCX", -[0xba] Iwdq,0, "MOV%S %i,%ODX", -[0xbb] Iwdq,0, "MOV%S %i,%OBX", -[0xbc] Iwdq,0, "MOV%S %i,%OSP", -[0xbd] Iwdq,0, "MOV%S %i,%OBP", -[0xbe] Iwdq,0, "MOV%S %i,%OSI", -[0xbf] Iwdq,0, "MOV%S %i,%ODI", -[0xc0] RMOPB,0, optabC0, -[0xc1] RMOP,0, optabC1, -[0xc2] Iw,0, "RET %i", -[0xc3] RET,0, "RET", -[0xc4] RM,0, "LES %e,%r", -[0xc5] RM,0, "LDS %e,%r", -[0xc6] RMB,Ib, "MOVB %i,%e", -[0xc7] RM,Iwd, "MOV%S %i,%e", -[0xc8] Iw2,Ib, "ENTER %i,%I", /* loony ENTER */ -[0xc9] RET,0, "LEAVE", /* bizarre LEAVE */ -[0xca] Iw,0, "RETF %i", -[0xcb] RET,0, "RETF", -[0xcc] 0,0, "INT 3", -[0xcd] Ib,0, "INTB %i", -[0xce] 0,0, "INTO", -[0xcf] 0,0, "IRET", -[0xd0] RMOPB,0, optabD0, -[0xd1] RMOP,0, optabD1, -[0xd2] RMOPB,0, optabD2, -[0xd3] RMOP,0, optabD3, -[0xd4] OA,0, "AAM", -[0xd5] OA,0, "AAD", -[0xd7] 0,0, "XLAT", -[0xd8] FRMOP,0, optabD8, -[0xd9] FRMEX,0, optabD9, -[0xda] FRMOP,0, optabDA, -[0xdb] FRMEX,0, optabDB, -[0xdc] FRMOP,0, optabDC, -[0xdd] FRMOP,0, optabDD, -[0xde] FRMOP,0, optabDE, -[0xdf] FRMOP,0, optabDF, -[0xe0] Jbs,0, "LOOPNE %p", -[0xe1] Jbs,0, "LOOPE %p", -[0xe2] Jbs,0, "LOOP %p", -[0xe3] Jbs,0, "JCXZ %p", -[0xe4] Ib,0, "INB %i,AL", -[0xe5] Ib,0, "IN%S %i,%OAX", -[0xe6] Ib,0, "OUTB AL,%i", -[0xe7] Ib,0, "OUT%S %OAX,%i", -[0xe8] Iwds,0, "CALL %p", -[0xe9] Iwds,0, "JMP %p", -[0xea] PTR,0, "JMP %d", -[0xeb] Jbs,0, "JMP %p", -[0xec] 0,0, "INB DX,AL", -[0xed] 0,0, "IN%S DX,%OAX", -[0xee] 0,0, "OUTB AL,DX", -[0xef] 0,0, "OUT%S %OAX,DX", -[0xf0] PRE,0, "LOCK", -[0xf2] OPRE,0, "REPNE", -[0xf3] OPRE,0, "REP", -[0xf4] 0,0, "HLT", -[0xf5] 0,0, "CMC", -[0xf6] RMOPB,0, optabF6, -[0xf7] RMOP,0, optabF7, -[0xf8] 0,0, "CLC", -[0xf9] 0,0, "STC", -[0xfa] 0,0, "CLI", -[0xfb] 0,0, "STI", -[0xfc] 0,0, "CLD", -[0xfd] 0,0, "STD", -[0xfe] RMOPB,0, optabFE, -[0xff] RMOP,0, optabFF, -[0x100] RM,0, "MOVLQSX %e,%r", -[0x101] RM,0, "MOVLQZX %e,%r", +[0x00] = { RMB,0, "ADDB %r,%e" }, +[0x01] = { RM,0, "ADD%S %r,%e" }, +[0x02] = { RMB,0, "ADDB %e,%r" }, +[0x03] = { RM,0, "ADD%S %e,%r" }, +[0x04] = { Ib,0, "ADDB %i,AL" }, +[0x05] = { Iwd,0, "ADD%S %i,%OAX" }, +[0x06] = { 0,0, "PUSHL ES" }, +[0x07] = { 0,0, "POPL ES" }, +[0x08] = { RMB,0, "ORB %r,%e" }, +[0x09] = { RM,0, "OR%S %r,%e" }, +[0x0a] = { RMB,0, "ORB %e,%r" }, +[0x0b] = { RM,0, "OR%S %e,%r" }, +[0x0c] = { Ib,0, "ORB %i,AL" }, +[0x0d] = { Iwd,0, "OR%S %i,%OAX" }, +[0x0e] = { 0,0, "PUSHL CS" }, +[0x0f] = { AUXMM,0, optab0F }, +[0x10] = { RMB,0, "ADCB %r,%e" }, +[0x11] = { RM,0, "ADC%S %r,%e" }, +[0x12] = { RMB,0, "ADCB %e,%r" }, +[0x13] = { RM,0, "ADC%S %e,%r" }, +[0x14] = { Ib,0, "ADCB %i,AL" }, +[0x15] = { Iwd,0, "ADC%S %i,%OAX" }, +[0x16] = { 0,0, "PUSHL SS" }, +[0x17] = { 0,0, "POPL SS" }, +[0x18] = { RMB,0, "SBBB %r,%e" }, +[0x19] = { RM,0, "SBB%S %r,%e" }, +[0x1a] = { RMB,0, "SBBB %e,%r" }, +[0x1b] = { RM,0, "SBB%S %e,%r" }, +[0x1c] = { Ib,0, "SBBB %i,AL" }, +[0x1d] = { Iwd,0, "SBB%S %i,%OAX" }, +[0x1e] = { 0,0, "PUSHL DS" }, +[0x1f] = { 0,0, "POPL DS" }, +[0x20] = { RMB,0, "ANDB %r,%e" }, +[0x21] = { RM,0, "AND%S %r,%e" }, +[0x22] = { RMB,0, "ANDB %e,%r" }, +[0x23] = { RM,0, "AND%S %e,%r" }, +[0x24] = { Ib,0, "ANDB %i,AL" }, +[0x25] = { Iwd,0, "AND%S %i,%OAX" }, +[0x26] = { SEG,0, "ES:" }, +[0x27] = { 0,0, "DAA" }, +[0x28] = { RMB,0, "SUBB %r,%e" }, +[0x29] = { RM,0, "SUB%S %r,%e" }, +[0x2a] = { RMB,0, "SUBB %e,%r" }, +[0x2b] = { RM,0, "SUB%S %e,%r" }, +[0x2c] = { Ib,0, "SUBB %i,AL" }, +[0x2d] = { Iwd,0, "SUB%S %i,%OAX" }, +[0x2e] = { SEG,0, "CS:" }, +[0x2f] = { 0,0, "DAS" }, +[0x30] = { RMB,0, "XORB %r,%e" }, +[0x31] = { RM,0, "XOR%S %r,%e" }, +[0x32] = { RMB,0, "XORB %e,%r" }, +[0x33] = { RM,0, "XOR%S %e,%r" }, +[0x34] = { Ib,0, "XORB %i,AL" }, +[0x35] = { Iwd,0, "XOR%S %i,%OAX" }, +[0x36] = { SEG,0, "SS:" }, +[0x37] = { 0,0, "AAA" }, +[0x38] = { RMB,0, "CMPB %r,%e" }, +[0x39] = { RM,0, "CMP%S %r,%e" }, +[0x3a] = { RMB,0, "CMPB %e,%r" }, +[0x3b] = { RM,0, "CMP%S %e,%r" }, +[0x3c] = { Ib,0, "CMPB %i,AL" }, +[0x3d] = { Iwd,0, "CMP%S %i,%OAX" }, +[0x3e] = { SEG,0, "DS:" }, +[0x3f] = { 0,0, "AAS" }, +[0x40] = { 0,0, "INC%S %OAX" }, +[0x41] = { 0,0, "INC%S %OCX" }, +[0x42] = { 0,0, "INC%S %ODX" }, +[0x43] = { 0,0, "INC%S %OBX" }, +[0x44] = { 0,0, "INC%S %OSP" }, +[0x45] = { 0,0, "INC%S %OBP" }, +[0x46] = { 0,0, "INC%S %OSI" }, +[0x47] = { 0,0, "INC%S %ODI" }, +[0x48] = { 0,0, "DEC%S %OAX" }, +[0x49] = { 0,0, "DEC%S %OCX" }, +[0x4a] = { 0,0, "DEC%S %ODX" }, +[0x4b] = { 0,0, "DEC%S %OBX" }, +[0x4c] = { 0,0, "DEC%S %OSP" }, +[0x4d] = { 0,0, "DEC%S %OBP" }, +[0x4e] = { 0,0, "DEC%S %OSI" }, +[0x4f] = { 0,0, "DEC%S %ODI" }, +[0x50] = { 0,0, "PUSH%S %OAX" }, +[0x51] = { 0,0, "PUSH%S %OCX" }, +[0x52] = { 0,0, "PUSH%S %ODX" }, +[0x53] = { 0,0, "PUSH%S %OBX" }, +[0x54] = { 0,0, "PUSH%S %OSP" }, +[0x55] = { 0,0, "PUSH%S %OBP" }, +[0x56] = { 0,0, "PUSH%S %OSI" }, +[0x57] = { 0,0, "PUSH%S %ODI" }, +[0x58] = { 0,0, "POP%S %OAX" }, +[0x59] = { 0,0, "POP%S %OCX" }, +[0x5a] = { 0,0, "POP%S %ODX" }, +[0x5b] = { 0,0, "POP%S %OBX" }, +[0x5c] = { 0,0, "POP%S %OSP" }, +[0x5d] = { 0,0, "POP%S %OBP" }, +[0x5e] = { 0,0, "POP%S %OSI" }, +[0x5f] = { 0,0, "POP%S %ODI" }, +[0x60] = { 0,0, "PUSHA%S" }, +[0x61] = { 0,0, "POPA%S" }, +[0x62] = { RMM,0, "BOUND %e,%r" }, +[0x63] = { RM,0, "ARPL %r,%e" }, +[0x64] = { SEG,0, "FS:" }, +[0x65] = { SEG,0, "GS:" }, +[0x66] = { OPOVER,0, "" }, +[0x67] = { ADDOVER,0, "" }, +[0x68] = { Iwd,0, "PUSH%S %i" }, +[0x69] = { RM,Iwd, "IMUL%S %e,%i,%r" }, +[0x6a] = { Ib,0, "PUSH%S %i" }, +[0x6b] = { RM,Ibs, "IMUL%S %e,%i,%r" }, +[0x6c] = { 0,0, "INSB DX,(%ODI)" }, +[0x6d] = { 0,0, "INS%S DX,(%ODI)" }, +[0x6e] = { 0,0, "OUTSB (%ASI),DX" }, +[0x6f] = { 0,0, "OUTS%S (%ASI),DX" }, +[0x70] = { Jbs,0, "JOS %p" }, +[0x71] = { Jbs,0, "JOC %p" }, +[0x72] = { Jbs,0, "JCS %p" }, +[0x73] = { Jbs,0, "JCC %p" }, +[0x74] = { Jbs,0, "JEQ %p" }, +[0x75] = { Jbs,0, "JNE %p" }, +[0x76] = { Jbs,0, "JLS %p" }, +[0x77] = { Jbs,0, "JHI %p" }, +[0x78] = { Jbs,0, "JMI %p" }, +[0x79] = { Jbs,0, "JPL %p" }, +[0x7a] = { Jbs,0, "JPS %p" }, +[0x7b] = { Jbs,0, "JPC %p" }, +[0x7c] = { Jbs,0, "JLT %p" }, +[0x7d] = { Jbs,0, "JGE %p" }, +[0x7e] = { Jbs,0, "JLE %p" }, +[0x7f] = { Jbs,0, "JGT %p" }, +[0x80] = { RMOPB,0, optab80 }, +[0x81] = { RMOP,0, optab81 }, +[0x83] = { RMOP,0, optab83 }, +[0x84] = { RMB,0, "TESTB %r,%e" }, +[0x85] = { RM,0, "TEST%S %r,%e" }, +[0x86] = { RMB,0, "XCHGB %r,%e" }, +[0x87] = { RM,0, "XCHG%S %r,%e" }, +[0x88] = { RMB,0, "MOVB %r,%e" }, +[0x89] = { RM,0, "MOV%S %r,%e" }, +[0x8a] = { RMB,0, "MOVB %e,%r" }, +[0x8b] = { RM,0, "MOV%S %e,%r" }, +[0x8c] = { RM,0, "MOVW %g,%e" }, +[0x8d] = { RM,0, "LEA%S %e,%r" }, +[0x8e] = { RM,0, "MOVW %e,%g" }, +[0x8f] = { RM,0, "POP%S %e" }, +[0x90] = { 0,0, "NOP" }, +[0x91] = { 0,0, "XCHG %OCX,%OAX" }, +[0x92] = { 0,0, "XCHG %ODX,%OAX" }, +[0x93] = { 0,0, "XCHG %OBX,%OAX" }, +[0x94] = { 0,0, "XCHG %OSP,%OAX" }, +[0x95] = { 0,0, "XCHG %OBP,%OAX" }, +[0x96] = { 0,0, "XCHG %OSI,%OAX" }, +[0x97] = { 0,0, "XCHG %ODI,%OAX" }, +[0x98] = { 0,0, "%W" }, /* miserable CBW or CWDE */ +[0x99] = { 0,0, "%w" }, /* idiotic CWD or CDQ */ +[0x9a] = { PTR,0, "CALL%S %d" }, +[0x9b] = { 0,0, "WAIT" }, +[0x9c] = { 0,0, "PUSHF" }, +[0x9d] = { 0,0, "POPF" }, +[0x9e] = { 0,0, "SAHF" }, +[0x9f] = { 0,0, "LAHF" }, +[0xa0] = { Awd,0, "MOVB %i,AL" }, +[0xa1] = { Awd,0, "MOV%S %i,%OAX" }, +[0xa2] = { Awd,0, "MOVB AL,%i" }, +[0xa3] = { Awd,0, "MOV%S %OAX,%i" }, +[0xa4] = { 0,0, "MOVSB (%ASI),(%ADI)" }, +[0xa5] = { 0,0, "MOVS%S (%ASI),(%ADI)" }, +[0xa6] = { 0,0, "CMPSB (%ASI),(%ADI)" }, +[0xa7] = { 0,0, "CMPS%S (%ASI),(%ADI)" }, +[0xa8] = { Ib,0, "TESTB %i,AL" }, +[0xa9] = { Iwd,0, "TEST%S %i,%OAX" }, +[0xaa] = { 0,0, "STOSB AL,(%ADI)" }, +[0xab] = { 0,0, "STOS%S %OAX,(%ADI)" }, +[0xac] = { 0,0, "LODSB (%ASI),AL" }, +[0xad] = { 0,0, "LODS%S (%ASI),%OAX" }, +[0xae] = { 0,0, "SCASB (%ADI),AL" }, +[0xaf] = { 0,0, "SCAS%S (%ADI),%OAX" }, +[0xb0] = { Ib,0, "MOVB %i,AL" }, +[0xb1] = { Ib,0, "MOVB %i,CL" }, +[0xb2] = { Ib,0, "MOVB %i,DL" }, +[0xb3] = { Ib,0, "MOVB %i,BL" }, +[0xb4] = { Ib,0, "MOVB %i,AH" }, +[0xb5] = { Ib,0, "MOVB %i,CH" }, +[0xb6] = { Ib,0, "MOVB %i,DH" }, +[0xb7] = { Ib,0, "MOVB %i,BH" }, +[0xb8] = { Iwdq,0, "MOV%S %i,%OAX" }, +[0xb9] = { Iwdq,0, "MOV%S %i,%OCX" }, +[0xba] = { Iwdq,0, "MOV%S %i,%ODX" }, +[0xbb] = { Iwdq,0, "MOV%S %i,%OBX" }, +[0xbc] = { Iwdq,0, "MOV%S %i,%OSP" }, +[0xbd] = { Iwdq,0, "MOV%S %i,%OBP" }, +[0xbe] = { Iwdq,0, "MOV%S %i,%OSI" }, +[0xbf] = { Iwdq,0, "MOV%S %i,%ODI" }, +[0xc0] = { RMOPB,0, optabC0 }, +[0xc1] = { RMOP,0, optabC1 }, +[0xc2] = { Iw,0, "RET %i" }, +[0xc3] = { RET,0, "RET" }, +[0xc4] = { RM,0, "LES %e,%r" }, +[0xc5] = { RM,0, "LDS %e,%r" }, +[0xc6] = { RMB,Ib, "MOVB %i,%e" }, +[0xc7] = { RM,Iwd, "MOV%S %i,%e" }, +[0xc8] = { Iw2,Ib, "ENTER %i,%I" }, /* loony ENTER */ +[0xc9] = { RET,0, "LEAVE" }, /* bizarre LEAVE */ +[0xca] = { Iw,0, "RETF %i" }, +[0xcb] = { RET,0, "RETF" }, +[0xcc] = { 0,0, "INT 3" }, +[0xcd] = { Ib,0, "INTB %i" }, +[0xce] = { 0,0, "INTO" }, +[0xcf] = { 0,0, "IRET" }, +[0xd0] = { RMOPB,0, optabD0 }, +[0xd1] = { RMOP,0, optabD1 }, +[0xd2] = { RMOPB,0, optabD2 }, +[0xd3] = { RMOP,0, optabD3 }, +[0xd4] = { OA,0, "AAM" }, +[0xd5] = { OA,0, "AAD" }, +[0xd7] = { 0,0, "XLAT" }, +[0xd8] = { FRMOP,0, optabD8 }, +[0xd9] = { FRMEX,0, optabD9 }, +[0xda] = { FRMOP,0, optabDA }, +[0xdb] = { FRMEX,0, optabDB }, +[0xdc] = { FRMOP,0, optabDC }, +[0xdd] = { FRMOP,0, optabDD }, +[0xde] = { FRMOP,0, optabDE }, +[0xdf] = { FRMOP,0, optabDF }, +[0xe0] = { Jbs,0, "LOOPNE %p" }, +[0xe1] = { Jbs,0, "LOOPE %p" }, +[0xe2] = { Jbs,0, "LOOP %p" }, +[0xe3] = { Jbs,0, "JCXZ %p" }, +[0xe4] = { Ib,0, "INB %i,AL" }, +[0xe5] = { Ib,0, "IN%S %i,%OAX" }, +[0xe6] = { Ib,0, "OUTB AL,%i" }, +[0xe7] = { Ib,0, "OUT%S %OAX,%i" }, +[0xe8] = { Iwds,0, "CALL %p" }, +[0xe9] = { Iwds,0, "JMP %p" }, +[0xea] = { PTR,0, "JMP %d" }, +[0xeb] = { Jbs,0, "JMP %p" }, +[0xec] = { 0,0, "INB DX,AL" }, +[0xed] = { 0,0, "IN%S DX,%OAX" }, +[0xee] = { 0,0, "OUTB AL,DX" }, +[0xef] = { 0,0, "OUT%S %OAX,DX" }, +[0xf0] = { PRE,0, "LOCK" }, +[0xf2] = { OPRE,0, "REPNE" }, +[0xf3] = { OPRE,0, "REP" }, +[0xf4] = { 0,0, "HLT" }, +[0xf5] = { 0,0, "CMC" }, +[0xf6] = { RMOPB,0, optabF6 }, +[0xf7] = { RMOP,0, optabF7 }, +[0xf8] = { 0,0, "CLC" }, +[0xf9] = { 0,0, "STC" }, +[0xfa] = { 0,0, "CLI" }, +[0xfb] = { 0,0, "STI" }, +[0xfc] = { 0,0, "CLD" }, +[0xfd] = { 0,0, "STD" }, +[0xfe] = { RMOPB,0, optabFE }, +[0xff] = { RMOP,0, optabFF }, +[0x100] = { RM,0, "MOVLQSX %e,%r" }, +[0x101] = { RM,0, "MOVLQZX %e,%r" }, }; /* @@ -1894,24 +1894,24 @@ bprint(Instr *ip, char *fmt, ...) #define ONAME(ip) "" static char *reg[] = { -[AX] "AX", -[CX] "CX", -[DX] "DX", -[BX] "BX", -[SP] "SP", -[BP] "BP", -[SI] "SI", -[DI] "DI", +[AX] = "AX", +[CX] = "CX", +[DX] = "DX", +[BX] = "BX", +[SP] = "SP", +[BP] = "BP", +[SI] = "SI", +[DI] = "DI", /* amd64 */ -[AMD64_R8] "R8", -[AMD64_R9] "R9", -[AMD64_R10] "R10", -[AMD64_R11] "R11", -[AMD64_R12] "R12", -[AMD64_R13] "R13", -[AMD64_R14] "R14", -[AMD64_R15] "R15", +[AMD64_R8] = "R8", +[AMD64_R9] = "R9", +[AMD64_R10] = "R10", +[AMD64_R11] = "R11", +[AMD64_R12] = "R12", +[AMD64_R13] = "R13", +[AMD64_R14] = "R14", +[AMD64_R15] = "R15", }; static char *breg[] = { "AL", "CL", "DL", "BL", "AH", "CH", "DH", "BH" }; diff --git a/src/libmach/obj.c b/src/libmach/obj.c index 7d660787b..7999f24c6 100644 --- a/src/libmach/obj.c +++ b/src/libmach/obj.c @@ -81,17 +81,17 @@ struct Obj /* functions to handle each intermediate (.$O) file */ static Obj obj[] = { /* functions to identify and parse each type of obj */ - [Obj68020] "68020 .2", _is2, _read2, - [ObjAmd64] "amd64 .6", _is6, _read6, - [ObjArm] "arm .5", _is5, _read5, - [ObjAlpha] "alpha .7", _is7, _read7, - [Obj386] "386 .8", _is8, _read8, - [ObjSparc] "sparc .k", _isk, _readk, - [ObjPower] "power .q", _isq, _readq, - [ObjMips] "mips .v", _isv, _readv, - [ObjSparc64] "sparc64 .u", _isu, _readu, - [ObjPower64] "power64 .9", _is9, _read9, - [Maxobjtype] 0, 0 + [Obj68020] = { "68020 .2", _is2, _read2 }, + [ObjAmd64] = { "amd64 .6", _is6 , _read6 }, + [ObjArm] = { "arm .5", _is5, _read5 }, + [ObjAlpha] = { "alpha .7", _is7, _read7 }, + [Obj386] = { "386 .8", _is8, _read8 }, + [ObjSparc] = { "sparc .k", _isk, _readk }, + [ObjPower] = { "power .q", _isq, _readq }, + [ObjMips] = { "mips .v", _isv, _readv }, + [ObjSparc64] = { "sparc64 .u", _isu, _readu }, + [ObjPower64] = { "power64 .9", _is9, _read9 }, + [Maxobjtype] = { 0, 0, 0 } }; struct Symtab diff --git a/src/pkg/Makefile b/src/pkg/Makefile index fc5548e98..2d6b3d014 100644 --- a/src/pkg/Makefile +++ b/src/pkg/Makefile @@ -59,7 +59,7 @@ DIRS=\ crypto/tls\ crypto/twofish\ crypto/x509\ - crypto/x509/crl\ + crypto/x509/pkix\ crypto/xtea\ debug/dwarf\ debug/macho\ @@ -77,13 +77,14 @@ DIRS=\ encoding/pem\ exec\ exp/datafmt\ - exp/draw\ - exp/draw/x11\ exp/eval\ + exp/gui\ + exp/gui/x11\ expvar\ flag\ fmt\ go/ast\ + go/build\ go/doc\ go/parser\ go/printer\ @@ -106,6 +107,7 @@ DIRS=\ http/spdy\ image\ image/bmp\ + image/draw\ image/gif\ image/jpeg\ image/png\ @@ -182,8 +184,10 @@ endif NOTEST+=\ crypto\ crypto/openpgp/error\ + crypto/x509/pkix\ debug/proc\ - exp/draw/x11\ + exp/gui\ + exp/gui/x11\ go/ast\ go/doc\ go/token\ diff --git a/src/pkg/big/int.go b/src/pkg/big/int.go index e66c34a83..22bdf8d2f 100755 --- a/src/pkg/big/int.go +++ b/src/pkg/big/int.go @@ -816,13 +816,13 @@ func (z *Int) Not(x *Int) *Int { // Gob codec version. Permits backward-compatible changes to the encoding. -const version byte = 1 +const intGobVersion byte = 1 // GobEncode implements the gob.GobEncoder interface. func (z *Int) GobEncode() ([]byte, os.Error) { - buf := make([]byte, len(z.abs)*_S+1) // extra byte for version and sign bit + buf := make([]byte, 1+len(z.abs)*_S) // extra byte for version and sign bit i := z.abs.bytes(buf) - 1 // i >= 0 - b := version << 1 // make space for sign bit + b := intGobVersion << 1 // make space for sign bit if z.neg { b |= 1 } @@ -837,7 +837,7 @@ func (z *Int) GobDecode(buf []byte) os.Error { return os.ErrorString("Int.GobDecode: no data") } b := buf[0] - if b>>1 != version { + if b>>1 != intGobVersion { return os.ErrorString(fmt.Sprintf("Int.GobDecode: encoding version %d not supported", b>>1)) } z.neg = b&1 != 0 diff --git a/src/pkg/big/int_test.go b/src/pkg/big/int_test.go index 1a492925b..58a55030d 100755 --- a/src/pkg/big/int_test.go +++ b/src/pkg/big/int_test.go @@ -1303,6 +1303,7 @@ func TestModInverse(t *testing.T) { } +// used by TestIntGobEncoding and TestRatGobEncoding var gobEncodingTests = []string{ "0", "1", @@ -1313,7 +1314,7 @@ var gobEncodingTests = []string{ "298472983472983471903246121093472394872319615612417471234712061", } -func TestGobEncoding(t *testing.T) { +func TestIntGobEncoding(t *testing.T) { var medium bytes.Buffer enc := gob.NewEncoder(&medium) dec := gob.NewDecoder(&medium) @@ -1321,7 +1322,8 @@ func TestGobEncoding(t *testing.T) { for j := 0; j < 2; j++ { medium.Reset() // empty buffer for each test case (in case of failures) stest := test - if j == 0 { + if j != 0 { + // negative numbers stest = "-" + test } var tx Int diff --git a/src/pkg/big/nat.go b/src/pkg/big/nat.go index fa09d6531..734568e06 100755 --- a/src/pkg/big/nat.go +++ b/src/pkg/big/nat.go @@ -615,6 +615,7 @@ func (x nat) bitLen() int { // MaxBase is the largest number base accepted for string conversions. const MaxBase = 'z' - 'a' + 10 + 1 // = hexValue('z') + 1 + func hexValue(ch int) Word { d := MaxBase + 1 // illegal base switch { @@ -733,46 +734,136 @@ const ( uppercaseDigits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" ) + +// decimalString returns a decimal representation of x. +// It calls x.string with the charset "0123456789". +func (x nat) decimalString() string { + return x.string(lowercaseDigits[0:10]) +} + + // string converts x to a string using digits from a charset; a digit with // value d is represented by charset[d]. The conversion base is determined // by len(charset), which must be >= 2. func (x nat) string(charset string) string { - base := len(charset) + b := Word(len(charset)) // special cases switch { - case base < 2: + case b < 2 || b > 256: panic("illegal base") case len(x) == 0: return string(charset[0]) } // allocate buffer for conversion - i := x.bitLen()/log2(Word(base)) + 1 // +1: round up + i := x.bitLen()/log2(b) + 1 // +1: round up s := make([]byte, i) - // don't destroy x + // special case: power of two bases can avoid divisions completely + if b == b&-b { + // shift is base-b digit size in bits + shift := uint(trailingZeroBits(b)) // shift > 0 because b >= 2 + mask := Word(1)<<shift - 1 + w := x[0] + nbits := uint(_W) // number of unprocessed bits in w + + // convert less-significant words + for k := 1; k < len(x); k++ { + // convert full digits + for nbits >= shift { + i-- + s[i] = charset[w&mask] + w >>= shift + nbits -= shift + } + + // convert any partial leading digit and advance to next word + if nbits == 0 { + // no partial digit remaining, just advance + w = x[k] + nbits = _W + } else { + // partial digit in current (k-1) and next (k) word + w |= x[k] << nbits + i-- + s[i] = charset[w&mask] + + // advance + w = x[k] >> (shift - nbits) + nbits = _W - (shift - nbits) + } + } + + // convert digits of most-significant word (omit leading zeros) + for nbits >= 0 && w != 0 { + i-- + s[i] = charset[w&mask] + w >>= shift + nbits -= shift + } + + return string(s[i:]) + } + + // general case: extract groups of digits by multiprecision division + + // maximize ndigits where b**ndigits < 2^_W; bb (big base) is b**ndigits + bb := Word(1) + ndigits := 0 + for max := Word(_M / b); bb <= max; bb *= b { + ndigits++ + } + + // preserve x, create local copy for use in repeated divisions q := nat(nil).set(x) + var r Word // convert - for len(q) > 0 { - i-- - var r Word - q, r = q.divW(q, Word(base)) - s[i] = charset[r] + if b == 10 { // hard-coding for 10 here speeds this up by 1.25x + for len(q) > 0 { + // extract least significant, base bb "digit" + q, r = q.divW(q, bb) // N.B. >82% of time is here. Optimize divW + if len(q) == 0 { + // skip leading zeros in most-significant group of digits + for j := 0; j < ndigits && r != 0; j++ { + i-- + s[i] = charset[r%10] + r /= 10 + } + } else { + for j := 0; j < ndigits; j++ { + i-- + s[i] = charset[r%10] + r /= 10 + } + } + } + } else { + for len(q) > 0 { + // extract least significant group of digits + q, r = q.divW(q, bb) // N.B. >82% of time is here. Optimize divW + if len(q) == 0 { + // skip leading zeros in most-significant group of digits + for j := 0; j < ndigits && r != 0; j++ { + i-- + s[i] = charset[r%b] + r /= b + } + } else { + for j := 0; j < ndigits; j++ { + i-- + s[i] = charset[r%b] + r /= b + } + } + } } return string(s[i:]) } -// decimalString returns a decimal representation of x. -// It calls x.string with the charset "0123456789". -func (x nat) decimalString() string { - return x.string(lowercaseDigits[0:10]) -} - - const deBruijn32 = 0x077CB531 var deBruijn32Lookup = []byte{ @@ -789,6 +880,7 @@ var deBruijn64Lookup = []byte{ 54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6, } + // trailingZeroBits returns the number of consecutive zero bits on the right // side of the given Word. // See Knuth, volume 4, section 7.3.1 @@ -960,7 +1052,9 @@ func (z nat) xor(x, y nat) nat { // greaterThan returns true iff (x1<<_W + x2) > (y1<<_W + y2) -func greaterThan(x1, x2, y1, y2 Word) bool { return x1 > y1 || x1 == y1 && x2 > y2 } +func greaterThan(x1, x2, y1, y2 Word) bool { + return x1 > y1 || x1 == y1 && x2 > y2 +} // modW returns x % d. diff --git a/src/pkg/big/nat_test.go b/src/pkg/big/nat_test.go index 50ea469be..fd93592dd 100755 --- a/src/pkg/big/nat_test.go +++ b/src/pkg/big/nat_test.go @@ -5,6 +5,7 @@ package big import ( + "fmt" "os" "strings" "testing" @@ -171,6 +172,36 @@ func BenchmarkMul(b *testing.B) { } +func toString(x nat, charset string) string { + base := len(charset) + + // special cases + switch { + case base < 2: + panic("illegal base") + case len(x) == 0: + return string(charset[0]) + } + + // allocate buffer for conversion + i := x.bitLen()/log2(Word(base)) + 1 // +1: round up + s := make([]byte, i) + + // don't destroy x + q := nat(nil).set(x) + + // convert + for len(q) > 0 { + i-- + var r Word + q, r = q.divW(q, Word(base)) + s[i] = charset[r] + } + + return string(s[i:]) +} + + var strTests = []struct { x nat // nat value to be converted c string // conversion charset @@ -360,6 +391,187 @@ func BenchmarkScanPi(b *testing.B) { } +const ( + // 314**271 + // base 2: 2249 digits + // base 8: 751 digits + // base 10: 678 digits + // base 16: 563 digits + shortBase = 314 + shortExponent = 271 + + // 3141**2178 + // base 2: 31577 digits + // base 8: 10527 digits + // base 10: 9507 digits + // base 16: 7895 digits + mediumBase = 3141 + mediumExponent = 2718 + + // 3141**2178 + // base 2: 406078 digits + // base 8: 135360 digits + // base 10: 122243 digits + // base 16: 101521 digits + longBase = 31415 + longExponent = 27182 +) + + +func BenchmarkScanShort2(b *testing.B) { + ScanHelper(b, 2, shortBase, shortExponent) +} + + +func BenchmarkScanShort8(b *testing.B) { + ScanHelper(b, 8, shortBase, shortExponent) +} + + +func BenchmarkScanSort10(b *testing.B) { + ScanHelper(b, 10, shortBase, shortExponent) +} + + +func BenchmarkScanShort16(b *testing.B) { + ScanHelper(b, 16, shortBase, shortExponent) +} + + +func BenchmarkScanMedium2(b *testing.B) { + ScanHelper(b, 2, mediumBase, mediumExponent) +} + + +func BenchmarkScanMedium8(b *testing.B) { + ScanHelper(b, 8, mediumBase, mediumExponent) +} + + +func BenchmarkScanMedium10(b *testing.B) { + ScanHelper(b, 10, mediumBase, mediumExponent) +} + + +func BenchmarkScanMedium16(b *testing.B) { + ScanHelper(b, 16, mediumBase, mediumExponent) +} + + +func BenchmarkScanLong2(b *testing.B) { + ScanHelper(b, 2, longBase, longExponent) +} + + +func BenchmarkScanLong8(b *testing.B) { + ScanHelper(b, 8, longBase, longExponent) +} + + +func BenchmarkScanLong10(b *testing.B) { + ScanHelper(b, 10, longBase, longExponent) +} + + +func BenchmarkScanLong16(b *testing.B) { + ScanHelper(b, 16, longBase, longExponent) +} + + +func ScanHelper(b *testing.B, base int, xv, yv Word) { + b.StopTimer() + var x, y, z nat + x = x.setWord(xv) + y = y.setWord(yv) + z = z.expNN(x, y, nil) + + var s string + s = z.string(lowercaseDigits[0:base]) + if t := toString(z, lowercaseDigits[0:base]); t != s { + panic(fmt.Sprintf("scanning: got %s; want %s", s, t)) + } + b.StartTimer() + + for i := 0; i < b.N; i++ { + x.scan(strings.NewReader(s), base) + } +} + + +func BenchmarkStringShort2(b *testing.B) { + StringHelper(b, 2, shortBase, shortExponent) +} + + +func BenchmarkStringShort8(b *testing.B) { + StringHelper(b, 8, shortBase, shortExponent) +} + + +func BenchmarkStringShort10(b *testing.B) { + StringHelper(b, 10, shortBase, shortExponent) +} + + +func BenchmarkStringShort16(b *testing.B) { + StringHelper(b, 16, shortBase, shortExponent) +} + + +func BenchmarkStringMedium2(b *testing.B) { + StringHelper(b, 2, mediumBase, mediumExponent) +} + + +func BenchmarkStringMedium8(b *testing.B) { + StringHelper(b, 8, mediumBase, mediumExponent) +} + + +func BenchmarkStringMedium10(b *testing.B) { + StringHelper(b, 10, mediumBase, mediumExponent) +} + + +func BenchmarkStringMedium16(b *testing.B) { + StringHelper(b, 16, mediumBase, mediumExponent) +} + + +func BenchmarkStringLong2(b *testing.B) { + StringHelper(b, 2, longBase, longExponent) +} + + +func BenchmarkStringLong8(b *testing.B) { + StringHelper(b, 8, longBase, longExponent) +} + + +func BenchmarkStringLong10(b *testing.B) { + StringHelper(b, 10, longBase, longExponent) +} + + +func BenchmarkStringLong16(b *testing.B) { + StringHelper(b, 16, longBase, longExponent) +} + + +func StringHelper(b *testing.B, base int, xv, yv Word) { + b.StopTimer() + var x, y, z nat + x = x.setWord(xv) + y = y.setWord(yv) + z = z.expNN(x, y, nil) + b.StartTimer() + + for i := 0; i < b.N; i++ { + z.string(lowercaseDigits[0:base]) + } +} + + func TestLeadingZeros(t *testing.T) { var x Word = _B >> 1 for i := 0; i <= _W; i++ { @@ -479,7 +691,6 @@ func TestTrailingZeroBits(t *testing.T) { } } - var expNNTests = []struct { x, y, m string out string diff --git a/src/pkg/big/rat.go b/src/pkg/big/rat.go index f11c27425..1fbf8c459 100644 --- a/src/pkg/big/rat.go +++ b/src/pkg/big/rat.go @@ -7,6 +7,7 @@ package big import ( + "encoding/binary" "fmt" "os" "strings" @@ -314,7 +315,11 @@ func (z *Rat) RatString() string { // digits of precision after the decimal point and the last digit rounded. func (z *Rat) FloatString(prec int) string { if z.IsInt() { - return z.a.String() + s := z.a.String() + if prec > 0 { + s += "." + strings.Repeat("0", prec) + } + return s } q, r := nat{}.div(nat{}, z.a.abs, z.b) @@ -350,3 +355,45 @@ func (z *Rat) FloatString(prec int) string { return s } + + +// Gob codec version. Permits backward-compatible changes to the encoding. +const ratGobVersion byte = 1 + +// GobEncode implements the gob.GobEncoder interface. +func (z *Rat) GobEncode() ([]byte, os.Error) { + buf := make([]byte, 1+4+(len(z.a.abs)+len(z.b))*_S) // extra bytes for version and sign bit (1), and numerator length (4) + i := z.b.bytes(buf) + j := z.a.abs.bytes(buf[0:i]) + n := i - j + if int(uint32(n)) != n { + // this should never happen + return nil, os.ErrorString("Rat.GobEncode: numerator too large") + } + binary.BigEndian.PutUint32(buf[j-4:j], uint32(n)) + j -= 1 + 4 + b := ratGobVersion << 1 // make space for sign bit + if z.a.neg { + b |= 1 + } + buf[j] = b + return buf[j:], nil +} + + +// GobDecode implements the gob.GobDecoder interface. +func (z *Rat) GobDecode(buf []byte) os.Error { + if len(buf) == 0 { + return os.ErrorString("Rat.GobDecode: no data") + } + b := buf[0] + if b>>1 != ratGobVersion { + return os.ErrorString(fmt.Sprintf("Rat.GobDecode: encoding version %d not supported", b>>1)) + } + const j = 1 + 4 + i := j + binary.BigEndian.Uint32(buf[j-4:j]) + z.a.neg = b&1 != 0 + z.a.abs = z.a.abs.setBytes(buf[j:i]) + z.b = z.b.setBytes(buf[i:]) + return nil +} diff --git a/src/pkg/big/rat_test.go b/src/pkg/big/rat_test.go index ae5c7c993..e64505ea3 100644 --- a/src/pkg/big/rat_test.go +++ b/src/pkg/big/rat_test.go @@ -7,6 +7,7 @@ package big import ( "bytes" "fmt" + "gob" "testing" ) @@ -86,12 +87,13 @@ var floatStringTests = []struct { out string }{ {"0", 0, "0"}, - {"0", 4, "0"}, + {"0", 4, "0.0000"}, {"1", 0, "1"}, - {"1", 2, "1"}, + {"1", 2, "1.00"}, {"-1", 0, "-1"}, {".25", 2, "0.25"}, {".25", 1, "0.3"}, + {".25", 3, "0.250"}, {"-1/3", 3, "-0.333"}, {"-2/3", 4, "-0.6667"}, {"0.96", 1, "1.0"}, @@ -307,3 +309,36 @@ func TestRatSetFrac64Rat(t *testing.T) { } } } + + +func TestRatGobEncoding(t *testing.T) { + var medium bytes.Buffer + enc := gob.NewEncoder(&medium) + dec := gob.NewDecoder(&medium) + for i, test := range gobEncodingTests { + for j := 0; j < 4; j++ { + medium.Reset() // empty buffer for each test case (in case of failures) + stest := test + if j&1 != 0 { + // negative numbers + stest = "-" + test + } + if j%2 != 0 { + // fractions + stest = stest + "." + test + } + var tx Rat + tx.SetString(stest) + if err := enc.Encode(&tx); err != nil { + t.Errorf("#%d%c: encoding failed: %s", i, 'a'+j, err) + } + var rx Rat + if err := dec.Decode(&rx); err != nil { + t.Errorf("#%d%c: decoding failed: %s", i, 'a'+j, err) + } + if rx.Cmp(&tx) != 0 { + t.Errorf("#%d%c: transmission failed: got %s want %s", i, 'a'+j, &rx, &tx) + } + } + } +} diff --git a/src/pkg/compress/lzw/reader.go b/src/pkg/compress/lzw/reader.go index a1cd2abc0..21231c8e5 100644 --- a/src/pkg/compress/lzw/reader.go +++ b/src/pkg/compress/lzw/reader.go @@ -32,13 +32,49 @@ const ( MSB ) +const ( + maxWidth = 12 + decoderInvalidCode = 0xffff + flushBuffer = 1 << maxWidth +) + // decoder is the state from which the readXxx method converts a byte // stream into a code stream. type decoder struct { - r io.ByteReader - bits uint32 - nBits uint - width uint + r io.ByteReader + bits uint32 + nBits uint + width uint + read func(*decoder) (uint16, os.Error) // readLSB or readMSB + litWidth int // width in bits of literal codes + err os.Error + + // The first 1<<litWidth codes are literal codes. + // The next two codes mean clear and EOF. + // Other valid codes are in the range [lo, hi] where lo := clear + 2, + // with the upper bound incrementing on each code seen. + // overflow is the code at which hi overflows the code width. + // last is the most recently seen code, or decoderInvalidCode. + clear, eof, hi, overflow, last uint16 + + // Each code c in [lo, hi] expands to two or more bytes. For c != hi: + // suffix[c] is the last of these bytes. + // prefix[c] is the code for all but the last byte. + // This code can either be a literal code or another code in [lo, c). + // The c == hi case is a special case. + suffix [1 << maxWidth]uint8 + prefix [1 << maxWidth]uint16 + + // output is the temporary output buffer. + // Literal codes are accumulated from the start of the buffer. + // Non-literal codes decode to a sequence of suffixes that are first + // written right-to-left from the end of the buffer before being copied + // to the start of the buffer. + // It is flushed when it contains >= 1<<maxWidth bytes, + // so that there is always room to decode an entire code. + output [2 * 1 << maxWidth]byte + o int // write index into output + toRead []byte // bytes to return from Read } // readLSB returns the next code for "Least Significant Bits first" data. @@ -73,119 +109,113 @@ func (d *decoder) readMSB() (uint16, os.Error) { return code, nil } -// decode decompresses bytes from r and writes them to pw. -// read specifies how to decode bytes into codes. -// litWidth is the width in bits of literal codes. -func decode(r io.Reader, read func(*decoder) (uint16, os.Error), litWidth int, pw *io.PipeWriter) { - br, ok := r.(io.ByteReader) - if !ok { - br = bufio.NewReader(r) +func (d *decoder) Read(b []byte) (int, os.Error) { + for { + if len(d.toRead) > 0 { + n := copy(b, d.toRead) + d.toRead = d.toRead[n:] + return n, nil + } + if d.err != nil { + return 0, d.err + } + d.decode() } - pw.CloseWithError(decode1(pw, br, read, uint(litWidth))) + panic("unreachable") } -func decode1(pw *io.PipeWriter, r io.ByteReader, read func(*decoder) (uint16, os.Error), litWidth uint) os.Error { - const ( - maxWidth = 12 - invalidCode = 0xffff - ) - d := decoder{r, 0, 0, 1 + litWidth} - w := bufio.NewWriter(pw) - // The first 1<<litWidth codes are literal codes. - // The next two codes mean clear and EOF. - // Other valid codes are in the range [lo, hi] where lo := clear + 2, - // with the upper bound incrementing on each code seen. - clear := uint16(1) << litWidth - eof, hi := clear+1, clear+1 - // overflow is the code at which hi overflows the code width. - overflow := uint16(1) << d.width - var ( - // Each code c in [lo, hi] expands to two or more bytes. For c != hi: - // suffix[c] is the last of these bytes. - // prefix[c] is the code for all but the last byte. - // This code can either be a literal code or another code in [lo, c). - // The c == hi case is a special case. - suffix [1 << maxWidth]uint8 - prefix [1 << maxWidth]uint16 - // buf is a scratch buffer for reconstituting the bytes that a code expands to. - // Code suffixes are written right-to-left from the end of the buffer. - buf [1 << maxWidth]byte - ) - +// decode decompresses bytes from r and leaves them in d.toRead. +// read specifies how to decode bytes into codes. +// litWidth is the width in bits of literal codes. +func (d *decoder) decode() { // Loop over the code stream, converting codes into decompressed bytes. - last := uint16(invalidCode) for { - code, err := read(&d) + code, err := d.read(d) if err != nil { if err == os.EOF { err = io.ErrUnexpectedEOF } - return err + d.err = err + return } switch { - case code < clear: + case code < d.clear: // We have a literal code. - if err := w.WriteByte(uint8(code)); err != nil { - return err - } - if last != invalidCode { + d.output[d.o] = uint8(code) + d.o++ + if d.last != decoderInvalidCode { // Save what the hi code expands to. - suffix[hi] = uint8(code) - prefix[hi] = last + d.suffix[d.hi] = uint8(code) + d.prefix[d.hi] = d.last } - case code == clear: - d.width = 1 + litWidth - hi = eof - overflow = 1 << d.width - last = invalidCode + case code == d.clear: + d.width = 1 + uint(d.litWidth) + d.hi = d.eof + d.overflow = 1 << d.width + d.last = decoderInvalidCode continue - case code == eof: - return w.Flush() - case code <= hi: - c, i := code, len(buf)-1 - if code == hi { + case code == d.eof: + d.flush() + d.err = os.EOF + return + case code <= d.hi: + c, i := code, len(d.output)-1 + if code == d.hi { // code == hi is a special case which expands to the last expansion // followed by the head of the last expansion. To find the head, we walk // the prefix chain until we find a literal code. - c = last - for c >= clear { - c = prefix[c] + c = d.last + for c >= d.clear { + c = d.prefix[c] } - buf[i] = uint8(c) + d.output[i] = uint8(c) i-- - c = last + c = d.last } - // Copy the suffix chain into buf and then write that to w. - for c >= clear { - buf[i] = suffix[c] + // Copy the suffix chain into output and then write that to w. + for c >= d.clear { + d.output[i] = d.suffix[c] i-- - c = prefix[c] + c = d.prefix[c] } - buf[i] = uint8(c) - if _, err := w.Write(buf[i:]); err != nil { - return err - } - if last != invalidCode { + d.output[i] = uint8(c) + d.o += copy(d.output[d.o:], d.output[i:]) + if d.last != decoderInvalidCode { // Save what the hi code expands to. - suffix[hi] = uint8(c) - prefix[hi] = last + d.suffix[d.hi] = uint8(c) + d.prefix[d.hi] = d.last } default: - return os.NewError("lzw: invalid code") + d.err = os.NewError("lzw: invalid code") + return } - last, hi = code, hi+1 - if hi >= overflow { + d.last, d.hi = code, d.hi+1 + if d.hi >= d.overflow { if d.width == maxWidth { - last = invalidCode - continue + d.last = decoderInvalidCode + } else { + d.width++ + d.overflow <<= 1 } - d.width++ - overflow <<= 1 + } + if d.o >= flushBuffer { + d.flush() + return } } panic("unreachable") } +func (d *decoder) flush() { + d.toRead = d.output[:d.o] + d.o = 0 +} + +func (d *decoder) Close() os.Error { + d.err = os.EINVAL // in case any Reads come along + return nil +} + // NewReader creates a new io.ReadCloser that satisfies reads by decompressing // the data read from r. // It is the caller's responsibility to call Close on the ReadCloser when @@ -193,21 +223,31 @@ func decode1(pw *io.PipeWriter, r io.ByteReader, read func(*decoder) (uint16, os // The number of bits to use for literal codes, litWidth, must be in the // range [2,8] and is typically 8. func NewReader(r io.Reader, order Order, litWidth int) io.ReadCloser { - pr, pw := io.Pipe() - var read func(*decoder) (uint16, os.Error) + d := new(decoder) switch order { case LSB: - read = (*decoder).readLSB + d.read = (*decoder).readLSB case MSB: - read = (*decoder).readMSB + d.read = (*decoder).readMSB default: - pw.CloseWithError(os.NewError("lzw: unknown order")) - return pr + d.err = os.NewError("lzw: unknown order") + return d } if litWidth < 2 || 8 < litWidth { - pw.CloseWithError(fmt.Errorf("lzw: litWidth %d out of range", litWidth)) - return pr + d.err = fmt.Errorf("lzw: litWidth %d out of range", litWidth) + return d } - go decode(r, read, litWidth, pw) - return pr + if br, ok := r.(io.ByteReader); ok { + d.r = br + } else { + d.r = bufio.NewReader(r) + } + d.litWidth = litWidth + d.width = 1 + uint(litWidth) + d.clear = uint16(1) << uint(litWidth) + d.eof, d.hi = d.clear+1, d.clear+1 + d.overflow = uint16(1) << d.width + d.last = decoderInvalidCode + + return d } diff --git a/src/pkg/compress/lzw/writer_test.go b/src/pkg/compress/lzw/writer_test.go index 82464ecd1..4c5e522f9 100644 --- a/src/pkg/compress/lzw/writer_test.go +++ b/src/pkg/compress/lzw/writer_test.go @@ -77,13 +77,13 @@ func testFile(t *testing.T, fn string, order Order, litWidth int) { t.Errorf("%s (order=%d litWidth=%d): %v", fn, order, litWidth, err1) return } - if len(b0) != len(b1) { - t.Errorf("%s (order=%d litWidth=%d): length mismatch %d versus %d", fn, order, litWidth, len(b0), len(b1)) + if len(b1) != len(b0) { + t.Errorf("%s (order=%d litWidth=%d): length mismatch %d != %d", fn, order, litWidth, len(b1), len(b0)) return } for i := 0; i < len(b0); i++ { - if b0[i] != b1[i] { - t.Errorf("%s (order=%d litWidth=%d): mismatch at %d, 0x%02x versus 0x%02x\n", fn, order, litWidth, i, b0[i], b1[i]) + if b1[i] != b0[i] { + t.Errorf("%s (order=%d litWidth=%d): mismatch at %d, 0x%02x != 0x%02x\n", fn, order, litWidth, i, b1[i], b0[i]) return } } diff --git a/src/pkg/crypto/ocsp/ocsp.go b/src/pkg/crypto/ocsp/ocsp.go index acd75b8b0..57dbe7d2d 100644 --- a/src/pkg/crypto/ocsp/ocsp.go +++ b/src/pkg/crypto/ocsp/ocsp.go @@ -13,6 +13,7 @@ import ( "crypto/rsa" _ "crypto/sha1" "crypto/x509" + "crypto/x509/pkix" "os" "time" ) @@ -32,21 +33,9 @@ const ( ocspUnauthorized = 5 ) -type rdnSequence []relativeDistinguishedNameSET - -type relativeDistinguishedNameSET []attributeTypeAndValue - -type attributeTypeAndValue struct { - Type asn1.ObjectIdentifier - Value interface{} -} - -type algorithmIdentifier struct { - Algorithm asn1.ObjectIdentifier -} type certID struct { - HashAlgorithm algorithmIdentifier + HashAlgorithm pkix.AlgorithmIdentifier NameHash []byte IssuerKeyHash []byte SerialNumber asn1.RawValue @@ -64,16 +53,16 @@ type responseBytes struct { type basicResponse struct { TBSResponseData responseData - SignatureAlgorithm algorithmIdentifier + SignatureAlgorithm pkix.AlgorithmIdentifier Signature asn1.BitString Certificates []asn1.RawValue "explicit,tag:0,optional" } type responseData struct { Raw asn1.RawContent - Version int "optional,default:1,explicit,tag:0" - RequestorName rdnSequence "optional,explicit,tag:1" - KeyHash []byte "optional,explicit,tag:2" + Version int "optional,default:1,explicit,tag:0" + RequestorName pkix.RDNSequence "optional,explicit,tag:1" + KeyHash []byte "optional,explicit,tag:2" ProducedAt *time.Time Responses []singleResponse } diff --git a/src/pkg/crypto/rsa/rsa.go b/src/pkg/crypto/rsa/rsa.go index 6bfe6c4e5..380f71570 100644 --- a/src/pkg/crypto/rsa/rsa.go +++ b/src/pkg/crypto/rsa/rsa.go @@ -64,7 +64,7 @@ func (priv *PrivateKey) Validate() os.Error { // easy for an attack to generate composites that pass this test. for _, prime := range priv.Primes { if !big.ProbablyPrime(prime, 20) { - return os.ErrorString("Prime factor is composite") + return os.ErrorString("prime factor is composite") } } diff --git a/src/pkg/crypto/tls/tls.go b/src/pkg/crypto/tls/tls.go index 7d0bb9f34..9e5c9270a 100644 --- a/src/pkg/crypto/tls/tls.go +++ b/src/pkg/crypto/tls/tls.go @@ -159,7 +159,7 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err os.Err key, err := x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes) if err != nil { - err = os.ErrorString("crypto/tls: failed to parse key") + err = os.ErrorString("crypto/tls: failed to parse key: " + err.String()) return } diff --git a/src/pkg/crypto/twofish/twofish.go b/src/pkg/crypto/twofish/twofish.go index 9303f03ff..1a1aac9b9 100644 --- a/src/pkg/crypto/twofish/twofish.go +++ b/src/pkg/crypto/twofish/twofish.go @@ -116,7 +116,7 @@ func (c *Cipher) Reset() { c.k[i] = 0 } for i := range c.s { - for j := 0; j < 265; j++ { + for j := 0; j < 256; j++ { c.s[i][j] = 0 } } diff --git a/src/pkg/crypto/x509/cert_pool.go b/src/pkg/crypto/x509/cert_pool.go index c295fd97e..16cd92efc 100644 --- a/src/pkg/crypto/x509/cert_pool.go +++ b/src/pkg/crypto/x509/cert_pool.go @@ -5,6 +5,7 @@ package x509 import ( + "crypto/x509/pkix" "encoding/pem" "strings" ) @@ -25,7 +26,7 @@ func NewCertPool() *CertPool { } } -func nameToKey(name *Name) string { +func nameToKey(name *pkix.Name) string { return strings.Join(name.Country, ",") + "/" + strings.Join(name.Organization, ",") + "/" + strings.Join(name.OrganizationalUnit, ",") + "/" + name.CommonName } diff --git a/src/pkg/crypto/x509/crl/crl.go b/src/pkg/crypto/x509/crl/crl.go deleted file mode 100644 index c79c797c7..000000000 --- a/src/pkg/crypto/x509/crl/crl.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2011 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 crl exposes low-level details of PKIX Certificate Revocation Lists -// as specified in RFC 5280, section 5. -package crl - -import ( - "asn1" - "bytes" - "encoding/pem" - "os" - "time" -) - -// CertificateList represents the ASN.1 structure of the same name. See RFC -// 5280, section 5.1. Use crypto/x509/Certificate.CheckCRLSignature to verify -// the signature. -type CertificateList struct { - TBSCertList TBSCertificateList - SignatureAlgorithm AlgorithmIdentifier - SignatureValue asn1.BitString -} - -// HasExpired returns true iff currentTimeSeconds is past the expiry time of -// certList. -func (certList *CertificateList) HasExpired(currentTimeSeconds int64) bool { - return certList.TBSCertList.NextUpdate.Seconds() <= currentTimeSeconds -} - -// TBSCertificateList represents the ASN.1 structure of the same name. See RFC -// 5280, section 5.1. -type TBSCertificateList struct { - Raw asn1.RawContent - Version int "optional,default:2" - Signature AlgorithmIdentifier - Issuer asn1.RawValue - ThisUpdate *time.Time - NextUpdate *time.Time - RevokedCertificates []RevokedCertificate "optional" - Extensions []Extension "tag:0,optional,explicit" -} - -// AlgorithmIdentifier represents the ASN.1 structure of the same name. See RFC -// 5280, section 4.1.1.2. -type AlgorithmIdentifier struct { - Algo asn1.ObjectIdentifier - Params asn1.RawValue "optional" -} - -// AlgorithmIdentifier represents the ASN.1 structure of the same name. See RFC -// 5280, section 5.1. -type RevokedCertificate struct { - SerialNumber asn1.RawValue - RevocationTime *time.Time - Extensions []Extension "optional" -} - -// AlgorithmIdentifier represents the ASN.1 structure of the same name. See RFC -// 5280, section 4.2. -type Extension struct { - Id asn1.ObjectIdentifier - IsCritial bool "optional" - Value []byte -} - -// pemCRLPrefix is the magic string that indicates that we have a PEM encoded -// CRL. -var pemCRLPrefix = []byte("-----BEGIN X509 CRL") -// pemType is the type of a PEM encoded CRL. -var pemType = "X509 CRL" - -// Parse parses a CRL from the given bytes. It's often the case that PEM -// encoded CRLs will appear where they should be DER encoded, so this function -// will transparently handle PEM encoding as long as there isn't any leading -// garbage. -func Parse(crlBytes []byte) (certList *CertificateList, err os.Error) { - if bytes.HasPrefix(crlBytes, pemCRLPrefix) { - block, _ := pem.Decode(crlBytes) - if block != nil && block.Type == pemType { - crlBytes = block.Bytes - } - } - return ParseDER(crlBytes) -} - -// ParseDER parses a DER encoded CRL from the given bytes. -func ParseDER(derBytes []byte) (certList *CertificateList, err os.Error) { - certList = new(CertificateList) - _, err = asn1.Unmarshal(derBytes, certList) - if err != nil { - certList = nil - } - return -} diff --git a/src/pkg/crypto/x509/crl/crl_test.go b/src/pkg/crypto/x509/crl/crl_test.go deleted file mode 100644 index 62d8dc195..000000000 --- a/src/pkg/crypto/x509/crl/crl_test.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2011 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 crl - -import ( - "encoding/base64" - "testing" -) - -func fromBase64(in string) []byte { - out := make([]byte, base64.StdEncoding.DecodedLen(len(in))) - _, err := base64.StdEncoding.Decode(out, []byte(in)) - if err != nil { - panic("failed to base64 decode") - } - return out -} - -func TestParseDER(t *testing.T) { - derBytes := fromBase64(derCRLBase64) - certList, err := ParseDER(derBytes) - if err != nil { - t.Errorf("error parsing: %s", err) - return - } - numCerts := len(certList.TBSCertList.RevokedCertificates) - expected := 88 - if numCerts != expected { - t.Errorf("bad number of revoked certificates. got: %d want: %d", numCerts, expected) - } - - if certList.HasExpired(1302517272) { - t.Errorf("CRL has expired (but shouldn't have)") - } - - // Can't check the signature here without a package cycle. -} - -func TestParsePEM(t *testing.T) { - pemBytes := fromBase64(pemCRLBase64) - certList, err := Parse(pemBytes) - if err != nil { - t.Errorf("error parsing: %s", err) - return - } - numCerts := len(certList.TBSCertList.RevokedCertificates) - expected := 2 - if numCerts != expected { - t.Errorf("bad number of revoked certificates. got: %d want: %d", numCerts, expected) - } - - if certList.HasExpired(1302517272) { - t.Errorf("CRL has expired (but shouldn't have)") - } - - // Can't check the signature here without a package cycle. -} - -const derCRLBase64 = "MIINqzCCDJMCAQEwDQYJKoZIhvcNAQEFBQAwVjEZMBcGA1UEAxMQUEtJIEZJTk1FQ0NBTklDQTEVMBMGA1UEChMMRklOTUVDQ0FOSUNBMRUwEwYDVQQLEwxGSU5NRUNDQU5JQ0ExCzAJBgNVBAYTAklUFw0xMTA1MDQxNjU3NDJaFw0xMTA1MDQyMDU3NDJaMIIMBzAhAg4Ze1od49Lt1qIXBydAzhcNMDkwNzE2MDg0MzIyWjAAMCECDl0HSL9bcZ1Ci/UHJ0DPFw0wOTA3MTYwODQzMTNaMAAwIQIOESB9tVAmX3cY7QcnQNAXDTA5MDcxNjA4NDUyMlowADAhAg4S1tGAQ3mHt8uVBydA1RcNMDkwODA0MTUyNTIyWjAAMCECDlQ249Y7vtC25ScHJ0DWFw0wOTA4MDQxNTI1MzdaMAAwIQIOISMop3NkA4PfYwcnQNkXDTA5MDgwNDExMDAzNFowADAhAg56/BMoS29KEShTBydA2hcNMDkwODA0MTEwMTAzWjAAMCECDnBp/22HPH5CSWoHJ0DbFw0wOTA4MDQxMDU0NDlaMAAwIQIOV9IP+8CD8bK+XAcnQNwXDTA5MDgwNDEwNTcxN1owADAhAg4v5aRz0IxWqYiXBydA3RcNMDkwODA0MTA1NzQ1WjAAMCECDlOU34VzvZAybQwHJ0DeFw0wOTA4MDQxMDU4MjFaMAAwIAINO4CD9lluIxcwBydBAxcNMDkwNzIyMTUzMTU5WjAAMCECDgOllfO8Y1QA7/wHJ0ExFw0wOTA3MjQxMTQxNDNaMAAwIQIOJBX7jbiCdRdyjgcnQUQXDTA5MDkxNjA5MzAwOFowADAhAg5iYSAgmDrlH/RZBydBRRcNMDkwOTE2MDkzMDE3WjAAMCECDmu6k6srP3jcMaQHJ0FRFw0wOTA4MDQxMDU2NDBaMAAwIQIOX8aHlO0V+WVH4QcnQVMXDTA5MDgwNDEwNTcyOVowADAhAg5flK2rg3NnsRgDBydBzhcNMTEwMjAxMTUzMzQ2WjAAMCECDg35yJDL1jOPTgoHJ0HPFw0xMTAyMDExNTM0MjZaMAAwIQIOMyFJ6+e9iiGVBQcnQdAXDTA5MDkxODEzMjAwNVowADAhAg5Emb/Oykucmn8fBydB1xcNMDkwOTIxMTAxMDQ3WjAAMCECDjQKCncV+MnUavMHJ0HaFw0wOTA5MjIwODE1MjZaMAAwIQIOaxiFUt3dpd+tPwcnQfQXDTEwMDYxODA4NDI1MVowADAhAg5G7P8nO0tkrMt7BydB9RcNMTAwNjE4MDg0MjMwWjAAMCECDmTCC3SXhmDRst4HJ0H2Fw0wOTA5MjgxMjA3MjBaMAAwIQIOHoGhUr/pRwzTKgcnQfcXDTA5MDkyODEyMDcyNFowADAhAg50wrcrCiw8mQmPBydCBBcNMTAwMjE2MTMwMTA2WjAAMCECDifWmkvwyhEqwEcHJ0IFFw0xMDAyMTYxMzAxMjBaMAAwIQIOfgPmlW9fg+osNgcnQhwXDTEwMDQxMzA5NTIwMFowADAhAg4YHAGuA6LgCk7tBydCHRcNMTAwNDEzMDk1MTM4WjAAMCECDi1zH1bxkNJhokAHJ0IsFw0xMDA0MTMwOTU5MzBaMAAwIQIOMipNccsb/wo2fwcnQi0XDTEwMDQxMzA5NTkwMFowADAhAg46lCmvPl4GpP6ABydCShcNMTAwMTE5MDk1MjE3WjAAMCECDjaTcaj+wBpcGAsHJ0JLFw0xMDAxMTkwOTUyMzRaMAAwIQIOOMC13EOrBuxIOQcnQloXDTEwMDIwMTA5NDcwNVowADAhAg5KmZl+krz4RsmrBydCWxcNMTAwMjAxMDk0NjQwWjAAMCECDmLG3zQJ/fzdSsUHJ0JiFw0xMDAzMDEwOTUxNDBaMAAwIQIOP39ksgHdojf4owcnQmMXDTEwMDMwMTA5NTExN1owADAhAg4LDQzvWNRlD6v9BydCZBcNMTAwMzAxMDk0NjIyWjAAMCECDkmNfeclaFhIaaUHJ0JlFw0xMDAzMDEwOTQ2MDVaMAAwIQIOT/qWWfpH/m8NTwcnQpQXDTEwMDUxMTA5MTgyMVowADAhAg5m/ksYxvCEgJSvBydClRcNMTAwNTExMDkxODAxWjAAMCECDgvf3Ohq6JOPU9AHJ0KWFw0xMDA1MTEwOTIxMjNaMAAwIQIOKSPas10z4jNVIQcnQpcXDTEwMDUxMTA5MjEwMlowADAhAg4mCWmhoZ3lyKCDBydCohcNMTEwNDI4MTEwMjI1WjAAMCECDkeiyRsBMK0Gvr4HJ0KjFw0xMTA0MjgxMTAyMDdaMAAwIQIOa09b/nH2+55SSwcnQq4XDTExMDQwMTA4Mjk0NlowADAhAg5O7M7iq7gGplr1BydCrxcNMTEwNDAxMDgzMDE3WjAAMCECDjlT6mJxUjTvyogHJ0K1Fw0xMTAxMjcxNTQ4NTJaMAAwIQIODS/l4UUFLe21NAcnQrYXDTExMDEyNzE1NDgyOFowADAhAg5lPRA0XdOUF6lSBydDHhcNMTEwMTI4MTQzNTA1WjAAMCECDixKX4fFGGpENwgHJ0MfFw0xMTAxMjgxNDM1MzBaMAAwIQIORNBkqsPnpKTtbAcnQ08XDTEwMDkwOTA4NDg0MlowADAhAg5QL+EMM3lohedEBydDUBcNMTAwOTA5MDg0ODE5WjAAMCECDlhDnHK+HiTRAXcHJ0NUFw0xMDEwMTkxNjIxNDBaMAAwIQIOdBFqAzq/INz53gcnQ1UXDTEwMTAxOTE2MjA0NFowADAhAg4OjR7s8MgKles1BydDWhcNMTEwMTI3MTY1MzM2WjAAMCECDmfR/elHee+d0SoHJ0NbFw0xMTAxMjcxNjUzNTZaMAAwIQIOBTKv2ui+KFMI+wcnQ5YXDTEwMDkxNTEwMjE1N1owADAhAg49F3c/GSah+oRUBydDmxcNMTEwMTI3MTczMjMzWjAAMCECDggv4I61WwpKFMMHJ0OcFw0xMTAxMjcxNzMyNTVaMAAwIQIOXx/Y8sEvwS10LAcnQ6UXDTExMDEyODExMjkzN1owADAhAg5LSLbnVrSKaw/9BydDphcNMTEwMTI4MTEyOTIwWjAAMCECDmFFoCuhKUeACQQHJ0PfFw0xMTAxMTExMDE3MzdaMAAwIQIOQTDdFh2fSPF6AAcnQ+AXDTExMDExMTEwMTcxMFowADAhAg5B8AOXX61FpvbbBydD5RcNMTAxMDA2MTAxNDM2WjAAMCECDh41P2Gmi7PkwI4HJ0PmFw0xMDEwMDYxMDE2MjVaMAAwIQIOWUHGLQCd+Ale9gcnQ/0XDTExMDUwMjA3NTYxMFowADAhAg5Z2c9AYkikmgWOBydD/hcNMTEwNTAyMDc1NjM0WjAAMCECDmf/UD+/h8nf+74HJ0QVFw0xMTA0MTUwNzI4MzNaMAAwIQIOICvj4epy3MrqfwcnRBYXDTExMDQxNTA3Mjg1NlowADAhAg4bouRMfOYqgv4xBydEHxcNMTEwMzA4MTYyNDI1WjAAMCECDhebWHGoKiTp7pEHJ0QgFw0xMTAzMDgxNjI0NDhaMAAwIQIOX+qnxxAqJ8LtawcnRDcXDTExMDEzMTE1MTIyOFowADAhAg4j0fICqZ+wkOdqBydEOBcNMTEwMTMxMTUxMTQxWjAAMCECDhmXjsV4SUpWtAMHJ0RLFw0xMTAxMjgxMTI0MTJaMAAwIQIODno/w+zG43kkTwcnREwXDTExMDEyODExMjM1MlowADAhAg4b1gc88767Fr+LBydETxcNMTEwMTI4MTEwMjA4WjAAMCECDn+M3Pa1w2nyFeUHJ0RQFw0xMTAxMjgxMDU4NDVaMAAwIQIOaduoyIH61tqybAcnRJUXDTEwMTIxNTA5NDMyMlowADAhAg4nLqQPkyi3ESAKBydElhcNMTAxMjE1MDk0MzM2WjAAMCECDi504NIMH8578gQHJ0SbFw0xMTAyMTQxNDA1NDFaMAAwIQIOGuaM8PDaC5u1egcnRJwXDTExMDIxNDE0MDYwNFowADAhAg4ehYq/BXGnB5PWBydEnxcNMTEwMjA0MDgwOTUxWjAAMCECDkSD4eS4FxW5H20HJ0SgFw0xMTAyMDQwODA5MjVaMAAwIQIOOCcb6ilYObt1egcnRKEXDTExMDEyNjEwNDEyOVowADAhAg58tISWCCwFnKGnBydEohcNMTEwMjA0MDgxMzQyWjAAMCECDn5rjtabY/L/WL0HJ0TJFw0xMTAyMDQxMTAzNDFaMAAwDQYJKoZIhvcNAQEFBQADggEBAGnF2Gs0+LNiYCW1Ipm83OXQYP/bd5tFFRzyz3iepFqNfYs4D68/QihjFoRHQoXEB0OEe1tvaVnnPGnEOpi6krwekquMxo4H88B5SlyiFIqemCOIss0SxlCFs69LmfRYvPPvPEhoXtQ3ZThe0UvKG83GOklhvGl6OaiRf4Mt+m8zOT4Wox/j6aOBK6cw6qKCdmD+Yj1rrNqFGg1CnSWMoD6S6mwNgkzwdBUJZ22BwrzAAo4RHa2Uy3ef1FjwD0XtU5N3uDSxGGBEDvOe5z82rps3E22FpAA8eYl8kaXtmWqyvYU0epp4brGuTxCuBMCAsxt/OjIjeNNQbBGkwxgfYA0=" - -const pemCRLBase64 = "LS0tLS1CRUdJTiBYNTA5IENSTC0tLS0tDQpNSUlCOWpDQ0FWOENBUUV3RFFZSktvWklodmNOQVFFRkJRQXdiREVhTUJnR0ExVUVDaE1SVWxOQklGTmxZM1Z5DQphWFI1SUVsdVl5NHhIakFjQmdOVkJBTVRGVkpUUVNCUWRXSnNhV01nVW05dmRDQkRRU0IyTVRFdU1Dd0dDU3FHDQpTSWIzRFFFSkFSWWZjbk5oYTJWdmJuSnZiM1J6YVdkdVFISnpZWE5sWTNWeWFYUjVMbU52YlJjTk1URXdNakl6DQpNVGt5T0RNd1doY05NVEV3T0RJeU1Ua3lPRE13V2pDQmpEQktBaEVBckRxb2g5RkhKSFhUN09QZ3V1bjQrQmNODQpNRGt4TVRBeU1UUXlOekE1V2pBbU1Bb0dBMVVkRlFRRENnRUpNQmdHQTFVZEdBUVJHQTh5TURBNU1URXdNakUwDQpNalExTlZvd1BnSVJBTEd6blowOTVQQjVhQU9MUGc1N2ZNTVhEVEF5TVRBeU16RTBOVEF4TkZvd0dqQVlCZ05WDQpIUmdFRVJnUE1qQXdNakV3TWpNeE5EVXdNVFJhb0RBd0xqQWZCZ05WSFNNRUdEQVdnQlQxVERGNlVRTS9MTmVMDQpsNWx2cUhHUXEzZzltekFMQmdOVkhSUUVCQUlDQUlRd0RRWUpLb1pJaHZjTkFRRUZCUUFEZ1lFQUZVNUFzNk16DQpxNVBSc2lmYW9iUVBHaDFhSkx5QytNczVBZ2MwYld5QTNHQWR4dXI1U3BQWmVSV0NCamlQL01FSEJXSkNsQkhQDQpHUmNxNXlJZDNFakRrYUV5eFJhK2k2N0x6dmhJNmMyOUVlNks5cFNZd2ppLzdSVWhtbW5Qclh0VHhsTDBsckxyDQptUVFKNnhoRFJhNUczUUE0Q21VZHNITnZicnpnbUNZcHZWRT0NCi0tLS0tRU5EIFg1MDkgQ1JMLS0tLS0NCg0K" diff --git a/src/pkg/crypto/x509/crl/Makefile b/src/pkg/crypto/x509/pkix/Makefile index d780af38e..e29b74c01 100644 --- a/src/pkg/crypto/x509/crl/Makefile +++ b/src/pkg/crypto/x509/pkix/Makefile @@ -4,8 +4,8 @@ include ../../../../Make.inc -TARG=crypto/x509/crl +TARG=crypto/x509/pkix GOFILES=\ - crl.go\ + pkix.go\ include ../../../../Make.pkg diff --git a/src/pkg/crypto/x509/pkix/pkix.go b/src/pkg/crypto/x509/pkix/pkix.go new file mode 100644 index 000000000..7806b2a2e --- /dev/null +++ b/src/pkg/crypto/x509/pkix/pkix.go @@ -0,0 +1,167 @@ +// Copyright 2011 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 pkix contains shared, low level structures used for ASN.1 parsing +// and serialization of X.509 certificates, CRL and OCSP. +package pkix + +import ( + "asn1" + "big" + "time" +) + +// AlgorithmIdentifier represents the ASN.1 structure of the same name. See RFC +// 5280, section 4.1.1.2. +type AlgorithmIdentifier struct { + Algorithm asn1.ObjectIdentifier + Parameters asn1.RawValue "optional" +} + +type RDNSequence []RelativeDistinguishedNameSET + +type RelativeDistinguishedNameSET []AttributeTypeAndValue + +type AttributeTypeAndValue struct { + Type asn1.ObjectIdentifier + Value interface{} +} + +// Extension represents the ASN.1 structure of the same name. See RFC +// 5280, section 4.2. +type Extension struct { + Id asn1.ObjectIdentifier + Critical bool "optional" + Value []byte +} + +// Name represents an X.509 distinguished name. This only includes the common +// elements of a DN. Additional elements in the name are ignored. +type Name struct { + Country, Organization, OrganizationalUnit []string + Locality, Province []string + StreetAddress, PostalCode []string + SerialNumber, CommonName string +} + +func (n *Name) FillFromRDNSequence(rdns *RDNSequence) { + for _, rdn := range *rdns { + if len(rdn) == 0 { + continue + } + atv := rdn[0] + value, ok := atv.Value.(string) + if !ok { + continue + } + + t := atv.Type + if len(t) == 4 && t[0] == 2 && t[1] == 5 && t[2] == 4 { + switch t[3] { + case 3: + n.CommonName = value + case 5: + n.SerialNumber = value + case 6: + n.Country = append(n.Country, value) + case 7: + n.Locality = append(n.Locality, value) + case 8: + n.Province = append(n.Province, value) + case 9: + n.StreetAddress = append(n.StreetAddress, value) + case 10: + n.Organization = append(n.Organization, value) + case 11: + n.OrganizationalUnit = append(n.OrganizationalUnit, value) + case 17: + n.PostalCode = append(n.PostalCode, value) + } + } + } +} + +var ( + oidCountry = []int{2, 5, 4, 6} + oidOrganization = []int{2, 5, 4, 10} + oidOrganizationalUnit = []int{2, 5, 4, 11} + oidCommonName = []int{2, 5, 4, 3} + oidSerialNumber = []int{2, 5, 4, 5} + oidLocality = []int{2, 5, 4, 7} + oidProvince = []int{2, 5, 4, 8} + oidStreetAddress = []int{2, 5, 4, 9} + oidPostalCode = []int{2, 5, 4, 17} +) + +// appendRDNs appends a relativeDistinguishedNameSET to the given RDNSequence +// and returns the new value. The relativeDistinguishedNameSET contains an +// attributeTypeAndValue for each of the given values. See RFC 5280, A.1, and +// search for AttributeTypeAndValue. +func appendRDNs(in RDNSequence, values []string, oid asn1.ObjectIdentifier) RDNSequence { + if len(values) == 0 { + return in + } + + s := make([]AttributeTypeAndValue, len(values)) + for i, value := range values { + s[i].Type = oid + s[i].Value = value + } + + return append(in, s) +} + +func (n Name) ToRDNSequence() (ret RDNSequence) { + ret = appendRDNs(ret, n.Country, oidCountry) + ret = appendRDNs(ret, n.Organization, oidOrganization) + ret = appendRDNs(ret, n.OrganizationalUnit, oidOrganizationalUnit) + ret = appendRDNs(ret, n.Locality, oidLocality) + ret = appendRDNs(ret, n.Province, oidProvince) + ret = appendRDNs(ret, n.StreetAddress, oidStreetAddress) + ret = appendRDNs(ret, n.PostalCode, oidPostalCode) + if len(n.CommonName) > 0 { + ret = appendRDNs(ret, []string{n.CommonName}, oidCommonName) + } + if len(n.SerialNumber) > 0 { + ret = appendRDNs(ret, []string{n.SerialNumber}, oidSerialNumber) + } + + return ret +} + +// CertificateList represents the ASN.1 structure of the same name. See RFC +// 5280, section 5.1. Use Certificate.CheckCRLSignature to verify the +// signature. +type CertificateList struct { + TBSCertList TBSCertificateList + SignatureAlgorithm AlgorithmIdentifier + SignatureValue asn1.BitString +} + +// HasExpired returns true iff currentTimeSeconds is past the expiry time of +// certList. +func (certList *CertificateList) HasExpired(currentTimeSeconds int64) bool { + return certList.TBSCertList.NextUpdate.Seconds() <= currentTimeSeconds +} + +// TBSCertificateList represents the ASN.1 structure of the same name. See RFC +// 5280, section 5.1. +type TBSCertificateList struct { + Raw asn1.RawContent + Version int "optional,default:2" + Signature AlgorithmIdentifier + Issuer RDNSequence + ThisUpdate *time.Time + NextUpdate *time.Time + RevokedCertificates []RevokedCertificate "optional" + Extensions []Extension "tag:0,optional,explicit" +} + +// RevokedCertificate represents the ASN.1 structure of the same name. See RFC +// 5280, section 5.1. +type RevokedCertificate struct { + SerialNumber *big.Int + RevocationTime *time.Time + Extensions []Extension "optional" +} diff --git a/src/pkg/crypto/x509/x509.go b/src/pkg/crypto/x509/x509.go index 6ae1f8e39..b10ffb0a2 100644 --- a/src/pkg/crypto/x509/x509.go +++ b/src/pkg/crypto/x509/x509.go @@ -14,7 +14,8 @@ import ( "crypto/dsa" "crypto/rsa" "crypto/sha1" - "crypto/x509/crl" + "crypto/x509/pkix" + "encoding/pem" "io" "os" "time" @@ -23,30 +24,25 @@ import ( // pkcs1PrivateKey is a structure which mirrors the PKCS#1 ASN.1 for an RSA private key. type pkcs1PrivateKey struct { Version int - N asn1.RawValue + N *big.Int E int - D asn1.RawValue - P asn1.RawValue - Q asn1.RawValue + D *big.Int + P *big.Int + Q *big.Int // We ignore these values, if present, because rsa will calculate them. - Dp asn1.RawValue "optional" - Dq asn1.RawValue "optional" - Qinv asn1.RawValue "optional" + Dp *big.Int "optional" + Dq *big.Int "optional" + Qinv *big.Int "optional" AdditionalPrimes []pkcs1AdditionalRSAPrime "optional" } type pkcs1AdditionalRSAPrime struct { - Prime asn1.RawValue + Prime *big.Int // We ignore these values because rsa will calculate them. - Exp asn1.RawValue - Coeff asn1.RawValue -} - -// rawValueIsInteger returns true iff the given ASN.1 RawValue is an INTEGER type. -func rawValueIsInteger(raw *asn1.RawValue) bool { - return raw.Class == 0 && raw.Tag == 2 && raw.IsCompound == false + Exp *big.Int + Coeff *big.Int } // ParsePKCS1PrivateKey returns an RSA private key from its ASN.1 PKCS#1 DER encoded form. @@ -65,29 +61,25 @@ func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err os.Error) { return nil, os.ErrorString("x509: unsupported private key version") } - if !rawValueIsInteger(&priv.N) || - !rawValueIsInteger(&priv.D) || - !rawValueIsInteger(&priv.P) || - !rawValueIsInteger(&priv.Q) { - err = asn1.StructuralError{"tags don't match"} - return + if priv.N.Sign() <= 0 || priv.D.Sign() <= 0 || priv.P.Sign() <= 0 || priv.Q.Sign() <= 0 { + return nil, os.ErrorString("private key contains zero or negative value") } key = new(rsa.PrivateKey) key.PublicKey = rsa.PublicKey{ E: priv.E, - N: new(big.Int).SetBytes(priv.N.Bytes), + N: priv.N, } - key.D = new(big.Int).SetBytes(priv.D.Bytes) + key.D = priv.D key.Primes = make([]*big.Int, 2+len(priv.AdditionalPrimes)) - key.Primes[0] = new(big.Int).SetBytes(priv.P.Bytes) - key.Primes[1] = new(big.Int).SetBytes(priv.Q.Bytes) + key.Primes[0] = priv.P + key.Primes[1] = priv.Q for i, a := range priv.AdditionalPrimes { - if !rawValueIsInteger(&a.Prime) { - return nil, asn1.StructuralError{"tags don't match"} + if a.Prime.Sign() <= 0 { + return nil, os.ErrorString("private key contains zero or negative prime") } - key.Primes[i+2] = new(big.Int).SetBytes(a.Prime.Bytes) + key.Primes[i+2] = a.Prime // We ignore the other two values because rsa will calculate // them as needed. } @@ -101,19 +93,6 @@ func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err os.Error) { return } -// rawValueForBig returns an asn1.RawValue which represents the given integer. -func rawValueForBig(n *big.Int) asn1.RawValue { - b := n.Bytes() - if n.Sign() >= 0 && len(b) > 0 && b[0]&0x80 != 0 { - // This positive number would be interpreted as a negative - // number in ASN.1 because the MSB is set. - padded := make([]byte, len(b)+1) - copy(padded[1:], b) - b = padded - } - return asn1.RawValue{Tag: 2, Bytes: b} -} - // MarshalPKCS1PrivateKey converts a private key to ASN.1 DER encoded form. func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte { key.Precompute() @@ -125,21 +104,21 @@ func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte { priv := pkcs1PrivateKey{ Version: version, - N: rawValueForBig(key.N), + N: key.N, E: key.PublicKey.E, - D: rawValueForBig(key.D), - P: rawValueForBig(key.Primes[0]), - Q: rawValueForBig(key.Primes[1]), - Dp: rawValueForBig(key.Precomputed.Dp), - Dq: rawValueForBig(key.Precomputed.Dq), - Qinv: rawValueForBig(key.Precomputed.Qinv), + D: key.D, + P: key.Primes[0], + Q: key.Primes[1], + Dp: key.Precomputed.Dp, + Dq: key.Precomputed.Dq, + Qinv: key.Precomputed.Qinv, } priv.AdditionalPrimes = make([]pkcs1AdditionalRSAPrime, len(key.Precomputed.CRTValues)) for i, values := range key.Precomputed.CRTValues { - priv.AdditionalPrimes[i].Prime = rawValueForBig(key.Primes[2+i]) - priv.AdditionalPrimes[i].Exp = rawValueForBig(values.Exp) - priv.AdditionalPrimes[i].Coeff = rawValueForBig(values.Coeff) + priv.AdditionalPrimes[i].Prime = key.Primes[2+i] + priv.AdditionalPrimes[i].Exp = values.Exp + priv.AdditionalPrimes[i].Coeff = values.Coeff } b, _ := asn1.Marshal(priv) @@ -151,44 +130,30 @@ func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte { type certificate struct { Raw asn1.RawContent TBSCertificate tbsCertificate - SignatureAlgorithm algorithmIdentifier + SignatureAlgorithm pkix.AlgorithmIdentifier SignatureValue asn1.BitString } type tbsCertificate struct { Raw asn1.RawContent Version int "optional,explicit,default:1,tag:0" - SerialNumber asn1.RawValue - SignatureAlgorithm algorithmIdentifier - Issuer rdnSequence + SerialNumber *big.Int + SignatureAlgorithm pkix.AlgorithmIdentifier + Issuer pkix.RDNSequence Validity validity - Subject rdnSequence + Subject pkix.RDNSequence PublicKey publicKeyInfo - UniqueId asn1.BitString "optional,tag:1" - SubjectUniqueId asn1.BitString "optional,tag:2" - Extensions []extension "optional,explicit,tag:3" + UniqueId asn1.BitString "optional,tag:1" + SubjectUniqueId asn1.BitString "optional,tag:2" + Extensions []pkix.Extension "optional,explicit,tag:3" } type dsaAlgorithmParameters struct { - P, Q, G asn1.RawValue + P, Q, G *big.Int } type dsaSignature struct { - R, S asn1.RawValue -} - -type algorithmIdentifier struct { - Algorithm asn1.ObjectIdentifier - Parameters asn1.RawValue "optional" -} - -type rdnSequence []relativeDistinguishedNameSET - -type relativeDistinguishedNameSET []attributeTypeAndValue - -type attributeTypeAndValue struct { - Type asn1.ObjectIdentifier - Value interface{} + R, S *big.Int } type validity struct { @@ -197,16 +162,10 @@ type validity struct { type publicKeyInfo struct { Raw asn1.RawContent - Algorithm algorithmIdentifier + Algorithm pkix.AlgorithmIdentifier PublicKey asn1.BitString } -type extension struct { - Id asn1.ObjectIdentifier - Critical bool "optional" - Value []byte -} - // RFC 5280, 4.2.1.1 type authKeyId struct { Id []byte "optional,tag:0" @@ -234,100 +193,6 @@ const ( DSA ) -// Name represents an X.509 distinguished name. This only includes the common -// elements of a DN. Additional elements in the name are ignored. -type Name struct { - Country, Organization, OrganizationalUnit []string - Locality, Province []string - StreetAddress, PostalCode []string - SerialNumber, CommonName string -} - -func (n *Name) fillFromRDNSequence(rdns *rdnSequence) { - for _, rdn := range *rdns { - if len(rdn) == 0 { - continue - } - atv := rdn[0] - value, ok := atv.Value.(string) - if !ok { - continue - } - - t := atv.Type - if len(t) == 4 && t[0] == 2 && t[1] == 5 && t[2] == 4 { - switch t[3] { - case 3: - n.CommonName = value - case 5: - n.SerialNumber = value - case 6: - n.Country = append(n.Country, value) - case 7: - n.Locality = append(n.Locality, value) - case 8: - n.Province = append(n.Province, value) - case 9: - n.StreetAddress = append(n.StreetAddress, value) - case 10: - n.Organization = append(n.Organization, value) - case 11: - n.OrganizationalUnit = append(n.OrganizationalUnit, value) - case 17: - n.PostalCode = append(n.PostalCode, value) - } - } - } -} - -var ( - oidCountry = []int{2, 5, 4, 6} - oidOrganization = []int{2, 5, 4, 10} - oidOrganizationalUnit = []int{2, 5, 4, 11} - oidCommonName = []int{2, 5, 4, 3} - oidSerialNumber = []int{2, 5, 4, 5} - oidLocality = []int{2, 5, 4, 7} - oidProvince = []int{2, 5, 4, 8} - oidStreetAddress = []int{2, 5, 4, 9} - oidPostalCode = []int{2, 5, 4, 17} -) - -// appendRDNs appends a relativeDistinguishedNameSET to the given rdnSequence -// and returns the new value. The relativeDistinguishedNameSET contains an -// attributeTypeAndValue for each of the given values. See RFC 5280, A.1, and -// search for AttributeTypeAndValue. -func appendRDNs(in rdnSequence, values []string, oid asn1.ObjectIdentifier) rdnSequence { - if len(values) == 0 { - return in - } - - s := make([]attributeTypeAndValue, len(values)) - for i, value := range values { - s[i].Type = oid - s[i].Value = value - } - - return append(in, s) -} - -func (n Name) toRDNSequence() (ret rdnSequence) { - ret = appendRDNs(ret, n.Country, oidCountry) - ret = appendRDNs(ret, n.Organization, oidOrganization) - ret = appendRDNs(ret, n.OrganizationalUnit, oidOrganizationalUnit) - ret = appendRDNs(ret, n.Locality, oidLocality) - ret = appendRDNs(ret, n.Province, oidProvince) - ret = appendRDNs(ret, n.StreetAddress, oidStreetAddress) - ret = appendRDNs(ret, n.PostalCode, oidPostalCode) - if len(n.CommonName) > 0 { - ret = appendRDNs(ret, []string{n.CommonName}, oidCommonName) - } - if len(n.SerialNumber) > 0 { - ret = appendRDNs(ret, []string{n.SerialNumber}, oidSerialNumber) - } - - return ret -} - // OIDs for signature algorithms // // pkcs-1 OBJECT IDENTIFIER ::= { @@ -483,9 +348,9 @@ type Certificate struct { PublicKey interface{} Version int - SerialNumber []byte - Issuer Name - Subject Name + SerialNumber *big.Int + Issuer pkix.Name + Subject pkix.Name NotBefore, NotAfter *time.Time // Validity bounds. KeyUsage KeyUsage @@ -591,12 +456,10 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature if _, err := asn1.Unmarshal(signature, dsaSig); err != nil { return err } - if !rawValueIsInteger(&dsaSig.R) || !rawValueIsInteger(&dsaSig.S) { - return asn1.StructuralError{"tags don't match"} + if dsaSig.R.Sign() <= 0 || dsaSig.S.Sign() <= 0 { + return os.ErrorString("DSA signature contained zero or negative values") } - r := new(big.Int).SetBytes(dsaSig.R.Bytes) - s := new(big.Int).SetBytes(dsaSig.S.Bytes) - if !dsa.Verify(pub, digest, r, s) { + if !dsa.Verify(pub, digest, dsaSig.R, dsaSig.S) { return os.ErrorString("DSA verification failure") } return @@ -605,8 +468,8 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature } // CheckCRLSignature checks that the signature in crl is from c. -func (c *Certificate) CheckCRLSignature(crl *crl.CertificateList) (err os.Error) { - algo := getSignatureAlgorithmFromOID(crl.SignatureAlgorithm.Algo) +func (c *Certificate) CheckCRLSignature(crl *pkix.CertificateList) (err os.Error) { + algo := getSignatureAlgorithmFromOID(crl.SignatureAlgorithm.Algorithm) return c.CheckSignature(algo, crl.TBSCertList.Raw, crl.SignatureValue.RightAlign()) } @@ -622,7 +485,7 @@ type basicConstraints struct { } type rsaPublicKey struct { - N asn1.RawValue + N *big.Int E int } @@ -654,42 +517,33 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{ return nil, err } - if !rawValueIsInteger(&p.N) { - return nil, asn1.StructuralError{"tags don't match"} - } - pub := &rsa.PublicKey{ E: p.E, - N: new(big.Int).SetBytes(p.N.Bytes), + N: p.N, } return pub, nil case DSA: - p := new(asn1.RawValue) - _, err := asn1.Unmarshal(asn1Data, p) + var p *big.Int + _, err := asn1.Unmarshal(asn1Data, &p) if err != nil { return nil, err } - if !rawValueIsInteger(p) { - return nil, asn1.StructuralError{"tags don't match"} - } paramsData := keyData.Algorithm.Parameters.FullBytes params := new(dsaAlgorithmParameters) _, err = asn1.Unmarshal(paramsData, params) if err != nil { return nil, err } - if !rawValueIsInteger(¶ms.P) || - !rawValueIsInteger(¶ms.Q) || - !rawValueIsInteger(¶ms.G) { - return nil, asn1.StructuralError{"tags don't match"} + if p.Sign() <= 0 || params.P.Sign() <= 0 || params.Q.Sign() <= 0 || params.G.Sign() <= 0 { + return nil, os.ErrorString("zero or negative DSA parameter") } pub := &dsa.PublicKey{ Parameters: dsa.Parameters{ - P: new(big.Int).SetBytes(params.P.Bytes), - Q: new(big.Int).SetBytes(params.Q.Bytes), - G: new(big.Int).SetBytes(params.G.Bytes), + P: params.P, + Q: params.Q, + G: params.G, }, - Y: new(big.Int).SetBytes(p.Bytes), + Y: p, } return pub, nil default: @@ -716,10 +570,14 @@ func parseCertificate(in *certificate) (*Certificate, os.Error) { return nil, err } + if in.TBSCertificate.SerialNumber.Sign() < 0 { + return nil, os.ErrorString("negative serial number") + } + out.Version = in.TBSCertificate.Version + 1 - out.SerialNumber = in.TBSCertificate.SerialNumber.Bytes - out.Issuer.fillFromRDNSequence(&in.TBSCertificate.Issuer) - out.Subject.fillFromRDNSequence(&in.TBSCertificate.Subject) + out.SerialNumber = in.TBSCertificate.SerialNumber + out.Issuer.FillFromRDNSequence(&in.TBSCertificate.Issuer) + out.Subject.FillFromRDNSequence(&in.TBSCertificate.Subject) out.NotBefore = in.TBSCertificate.Validity.NotBefore out.NotAfter = in.TBSCertificate.Validity.NotAfter @@ -977,8 +835,8 @@ var ( oidExtensionNameConstraints = []int{2, 5, 29, 30} ) -func buildExtensions(template *Certificate) (ret []extension, err os.Error) { - ret = make([]extension, 7 /* maximum number of elements. */ ) +func buildExtensions(template *Certificate) (ret []pkix.Extension, err os.Error) { + ret = make([]pkix.Extension, 7 /* maximum number of elements. */ ) n := 0 if template.KeyUsage != 0 { @@ -1095,7 +953,7 @@ var ( // The returned slice is the certificate in DER encoding. func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.PublicKey, priv *rsa.PrivateKey) (cert []byte, err os.Error) { asn1PublicKey, err := asn1.Marshal(rsaPublicKey{ - N: asn1.RawValue{Tag: 2, Bytes: pub.N.Bytes()}, + N: pub.N, E: pub.E, }) if err != nil { @@ -1114,12 +972,12 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.P encodedPublicKey := asn1.BitString{BitLength: len(asn1PublicKey) * 8, Bytes: asn1PublicKey} c := tbsCertificate{ Version: 2, - SerialNumber: asn1.RawValue{Bytes: template.SerialNumber, Tag: 2}, - SignatureAlgorithm: algorithmIdentifier{Algorithm: oidSHA1WithRSA}, - Issuer: parent.Subject.toRDNSequence(), + SerialNumber: template.SerialNumber, + SignatureAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidSHA1WithRSA}, + Issuer: parent.Subject.ToRDNSequence(), Validity: validity{template.NotBefore, template.NotAfter}, - Subject: template.Subject.toRDNSequence(), - PublicKey: publicKeyInfo{nil, algorithmIdentifier{Algorithm: oidRSA}, encodedPublicKey}, + Subject: template.Subject.ToRDNSequence(), + PublicKey: publicKeyInfo{nil, pkix.AlgorithmIdentifier{Algorithm: oidRSA}, encodedPublicKey}, Extensions: extensions, } @@ -1142,8 +1000,75 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.P cert, err = asn1.Marshal(certificate{ nil, c, - algorithmIdentifier{Algorithm: oidSHA1WithRSA}, + pkix.AlgorithmIdentifier{Algorithm: oidSHA1WithRSA}, asn1.BitString{Bytes: signature, BitLength: len(signature) * 8}, }) return } + +// pemCRLPrefix is the magic string that indicates that we have a PEM encoded +// CRL. +var pemCRLPrefix = []byte("-----BEGIN X509 CRL") +// pemType is the type of a PEM encoded CRL. +var pemType = "X509 CRL" + +// ParseCRL parses a CRL from the given bytes. It's often the case that PEM +// encoded CRLs will appear where they should be DER encoded, so this function +// will transparently handle PEM encoding as long as there isn't any leading +// garbage. +func ParseCRL(crlBytes []byte) (certList *pkix.CertificateList, err os.Error) { + if bytes.HasPrefix(crlBytes, pemCRLPrefix) { + block, _ := pem.Decode(crlBytes) + if block != nil && block.Type == pemType { + crlBytes = block.Bytes + } + } + return ParseDERCRL(crlBytes) +} + +// ParseDERCRL parses a DER encoded CRL from the given bytes. +func ParseDERCRL(derBytes []byte) (certList *pkix.CertificateList, err os.Error) { + certList = new(pkix.CertificateList) + _, err = asn1.Unmarshal(derBytes, certList) + if err != nil { + certList = nil + } + return +} + +// CreateCRL returns a DER encoded CRL, signed by this Certificate, that +// contains the given list of revoked certificates. +func (c *Certificate) CreateCRL(rand io.Reader, priv *rsa.PrivateKey, revokedCerts []pkix.RevokedCertificate, now, expiry *time.Time) (crlBytes []byte, err os.Error) { + tbsCertList := pkix.TBSCertificateList{ + Version: 2, + Signature: pkix.AlgorithmIdentifier{ + Algorithm: oidSignatureSHA1WithRSA, + }, + Issuer: c.Subject.ToRDNSequence(), + ThisUpdate: now, + NextUpdate: expiry, + RevokedCertificates: revokedCerts, + } + + tbsCertListContents, err := asn1.Marshal(tbsCertList) + if err != nil { + return + } + + h := sha1.New() + h.Write(tbsCertListContents) + digest := h.Sum() + + signature, err := rsa.SignPKCS1v15(rand, priv, crypto.SHA1, digest) + if err != nil { + return + } + + return asn1.Marshal(pkix.CertificateList{ + TBSCertList: tbsCertList, + SignatureAlgorithm: pkix.AlgorithmIdentifier{ + Algorithm: oidSignatureSHA1WithRSA, + }, + SignatureValue: asn1.BitString{Bytes: signature, BitLength: len(signature) * 8}, + }) +} diff --git a/src/pkg/crypto/x509/x509_test.go b/src/pkg/crypto/x509/x509_test.go index fd137a6f5..dc216505e 100644 --- a/src/pkg/crypto/x509/x509_test.go +++ b/src/pkg/crypto/x509/x509_test.go @@ -10,6 +10,8 @@ import ( "crypto/dsa" "crypto/rand" "crypto/rsa" + "crypto/x509/pkix" + "encoding/base64" "encoding/hex" "encoding/pem" "testing" @@ -207,8 +209,8 @@ func TestCreateSelfSignedCertificate(t *testing.T) { } template := Certificate{ - SerialNumber: []byte{1}, - Subject: Name{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ CommonName: "test.example.com", Organization: []string{"Acme Co"}, }, @@ -331,3 +333,99 @@ func TestVerifyCertificateWithDSASignature(t *testing.T) { t.Fatalf("DSA Certificate verfication failed: %s", err) } } + +const pemCertificate = `-----BEGIN CERTIFICATE----- +MIIB5DCCAZCgAwIBAgIBATALBgkqhkiG9w0BAQUwLTEQMA4GA1UEChMHQWNtZSBDbzEZMBcGA1UE +AxMQdGVzdC5leGFtcGxlLmNvbTAeFw03MDAxMDEwMDE2NDBaFw03MDAxMDIwMzQ2NDBaMC0xEDAO +BgNVBAoTB0FjbWUgQ28xGTAXBgNVBAMTEHRlc3QuZXhhbXBsZS5jb20wWjALBgkqhkiG9w0BAQED +SwAwSAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0fd7Ai2KW5ToIwzFo +fvJcS/STa6HA5gQenRUCAwEAAaOBnjCBmzAOBgNVHQ8BAf8EBAMCAAQwDwYDVR0TAQH/BAUwAwEB +/zANBgNVHQ4EBgQEAQIDBDAPBgNVHSMECDAGgAQBAgMEMBsGA1UdEQQUMBKCEHRlc3QuZXhhbXBs +ZS5jb20wDwYDVR0gBAgwBjAEBgIqAzAqBgNVHR4EIzAhoB8wDoIMLmV4YW1wbGUuY29tMA2CC2V4 +YW1wbGUuY29tMAsGCSqGSIb3DQEBBQNBAHKZKoS1wEQOGhgklx4+/yFYQlnqwKXvar/ZecQvJwui +0seMQnwBhwdBkHfVIU2Fu5VUMRyxlf0ZNaDXcpU581k= +-----END CERTIFICATE-----` + +func TestCRLCreation(t *testing.T) { + block, _ := pem.Decode([]byte(pemPrivateKey)) + priv, _ := ParsePKCS1PrivateKey(block.Bytes) + block, _ = pem.Decode([]byte(pemCertificate)) + cert, _ := ParseCertificate(block.Bytes) + + now := time.SecondsToUTC(1000) + expiry := time.SecondsToUTC(10000) + + revokedCerts := []pkix.RevokedCertificate{ + { + SerialNumber: big.NewInt(1), + RevocationTime: now, + }, + { + SerialNumber: big.NewInt(42), + RevocationTime: now, + }, + } + + crlBytes, err := cert.CreateCRL(rand.Reader, priv, revokedCerts, now, expiry) + if err != nil { + t.Errorf("error creating CRL: %s", err) + } + + _, err = ParseDERCRL(crlBytes) + if err != nil { + t.Errorf("error reparsing CRL: %s", err) + } +} + +func fromBase64(in string) []byte { + out := make([]byte, base64.StdEncoding.DecodedLen(len(in))) + _, err := base64.StdEncoding.Decode(out, []byte(in)) + if err != nil { + panic("failed to base64 decode") + } + return out +} + +func TestParseDERCRL(t *testing.T) { + derBytes := fromBase64(derCRLBase64) + certList, err := ParseDERCRL(derBytes) + if err != nil { + t.Errorf("error parsing: %s", err) + return + } + numCerts := len(certList.TBSCertList.RevokedCertificates) + expected := 88 + if numCerts != expected { + t.Errorf("bad number of revoked certificates. got: %d want: %d", numCerts, expected) + } + + if certList.HasExpired(1302517272) { + t.Errorf("CRL has expired (but shouldn't have)") + } + + // Can't check the signature here without a package cycle. +} + +func TestParsePEMCRL(t *testing.T) { + pemBytes := fromBase64(pemCRLBase64) + certList, err := ParseCRL(pemBytes) + if err != nil { + t.Errorf("error parsing: %s", err) + return + } + numCerts := len(certList.TBSCertList.RevokedCertificates) + expected := 2 + if numCerts != expected { + t.Errorf("bad number of revoked certificates. got: %d want: %d", numCerts, expected) + } + + if certList.HasExpired(1302517272) { + t.Errorf("CRL has expired (but shouldn't have)") + } + + // Can't check the signature here without a package cycle. +} + +const derCRLBase64 = "MIINqzCCDJMCAQEwDQYJKoZIhvcNAQEFBQAwVjEZMBcGA1UEAxMQUEtJIEZJTk1FQ0NBTklDQTEVMBMGA1UEChMMRklOTUVDQ0FOSUNBMRUwEwYDVQQLEwxGSU5NRUNDQU5JQ0ExCzAJBgNVBAYTAklUFw0xMTA1MDQxNjU3NDJaFw0xMTA1MDQyMDU3NDJaMIIMBzAhAg4Ze1od49Lt1qIXBydAzhcNMDkwNzE2MDg0MzIyWjAAMCECDl0HSL9bcZ1Ci/UHJ0DPFw0wOTA3MTYwODQzMTNaMAAwIQIOESB9tVAmX3cY7QcnQNAXDTA5MDcxNjA4NDUyMlowADAhAg4S1tGAQ3mHt8uVBydA1RcNMDkwODA0MTUyNTIyWjAAMCECDlQ249Y7vtC25ScHJ0DWFw0wOTA4MDQxNTI1MzdaMAAwIQIOISMop3NkA4PfYwcnQNkXDTA5MDgwNDExMDAzNFowADAhAg56/BMoS29KEShTBydA2hcNMDkwODA0MTEwMTAzWjAAMCECDnBp/22HPH5CSWoHJ0DbFw0wOTA4MDQxMDU0NDlaMAAwIQIOV9IP+8CD8bK+XAcnQNwXDTA5MDgwNDEwNTcxN1owADAhAg4v5aRz0IxWqYiXBydA3RcNMDkwODA0MTA1NzQ1WjAAMCECDlOU34VzvZAybQwHJ0DeFw0wOTA4MDQxMDU4MjFaMAAwIAINO4CD9lluIxcwBydBAxcNMDkwNzIyMTUzMTU5WjAAMCECDgOllfO8Y1QA7/wHJ0ExFw0wOTA3MjQxMTQxNDNaMAAwIQIOJBX7jbiCdRdyjgcnQUQXDTA5MDkxNjA5MzAwOFowADAhAg5iYSAgmDrlH/RZBydBRRcNMDkwOTE2MDkzMDE3WjAAMCECDmu6k6srP3jcMaQHJ0FRFw0wOTA4MDQxMDU2NDBaMAAwIQIOX8aHlO0V+WVH4QcnQVMXDTA5MDgwNDEwNTcyOVowADAhAg5flK2rg3NnsRgDBydBzhcNMTEwMjAxMTUzMzQ2WjAAMCECDg35yJDL1jOPTgoHJ0HPFw0xMTAyMDExNTM0MjZaMAAwIQIOMyFJ6+e9iiGVBQcnQdAXDTA5MDkxODEzMjAwNVowADAhAg5Emb/Oykucmn8fBydB1xcNMDkwOTIxMTAxMDQ3WjAAMCECDjQKCncV+MnUavMHJ0HaFw0wOTA5MjIwODE1MjZaMAAwIQIOaxiFUt3dpd+tPwcnQfQXDTEwMDYxODA4NDI1MVowADAhAg5G7P8nO0tkrMt7BydB9RcNMTAwNjE4MDg0MjMwWjAAMCECDmTCC3SXhmDRst4HJ0H2Fw0wOTA5MjgxMjA3MjBaMAAwIQIOHoGhUr/pRwzTKgcnQfcXDTA5MDkyODEyMDcyNFowADAhAg50wrcrCiw8mQmPBydCBBcNMTAwMjE2MTMwMTA2WjAAMCECDifWmkvwyhEqwEcHJ0IFFw0xMDAyMTYxMzAxMjBaMAAwIQIOfgPmlW9fg+osNgcnQhwXDTEwMDQxMzA5NTIwMFowADAhAg4YHAGuA6LgCk7tBydCHRcNMTAwNDEzMDk1MTM4WjAAMCECDi1zH1bxkNJhokAHJ0IsFw0xMDA0MTMwOTU5MzBaMAAwIQIOMipNccsb/wo2fwcnQi0XDTEwMDQxMzA5NTkwMFowADAhAg46lCmvPl4GpP6ABydCShcNMTAwMTE5MDk1MjE3WjAAMCECDjaTcaj+wBpcGAsHJ0JLFw0xMDAxMTkwOTUyMzRaMAAwIQIOOMC13EOrBuxIOQcnQloXDTEwMDIwMTA5NDcwNVowADAhAg5KmZl+krz4RsmrBydCWxcNMTAwMjAxMDk0NjQwWjAAMCECDmLG3zQJ/fzdSsUHJ0JiFw0xMDAzMDEwOTUxNDBaMAAwIQIOP39ksgHdojf4owcnQmMXDTEwMDMwMTA5NTExN1owADAhAg4LDQzvWNRlD6v9BydCZBcNMTAwMzAxMDk0NjIyWjAAMCECDkmNfeclaFhIaaUHJ0JlFw0xMDAzMDEwOTQ2MDVaMAAwIQIOT/qWWfpH/m8NTwcnQpQXDTEwMDUxMTA5MTgyMVowADAhAg5m/ksYxvCEgJSvBydClRcNMTAwNTExMDkxODAxWjAAMCECDgvf3Ohq6JOPU9AHJ0KWFw0xMDA1MTEwOTIxMjNaMAAwIQIOKSPas10z4jNVIQcnQpcXDTEwMDUxMTA5MjEwMlowADAhAg4mCWmhoZ3lyKCDBydCohcNMTEwNDI4MTEwMjI1WjAAMCECDkeiyRsBMK0Gvr4HJ0KjFw0xMTA0MjgxMTAyMDdaMAAwIQIOa09b/nH2+55SSwcnQq4XDTExMDQwMTA4Mjk0NlowADAhAg5O7M7iq7gGplr1BydCrxcNMTEwNDAxMDgzMDE3WjAAMCECDjlT6mJxUjTvyogHJ0K1Fw0xMTAxMjcxNTQ4NTJaMAAwIQIODS/l4UUFLe21NAcnQrYXDTExMDEyNzE1NDgyOFowADAhAg5lPRA0XdOUF6lSBydDHhcNMTEwMTI4MTQzNTA1WjAAMCECDixKX4fFGGpENwgHJ0MfFw0xMTAxMjgxNDM1MzBaMAAwIQIORNBkqsPnpKTtbAcnQ08XDTEwMDkwOTA4NDg0MlowADAhAg5QL+EMM3lohedEBydDUBcNMTAwOTA5MDg0ODE5WjAAMCECDlhDnHK+HiTRAXcHJ0NUFw0xMDEwMTkxNjIxNDBaMAAwIQIOdBFqAzq/INz53gcnQ1UXDTEwMTAxOTE2MjA0NFowADAhAg4OjR7s8MgKles1BydDWhcNMTEwMTI3MTY1MzM2WjAAMCECDmfR/elHee+d0SoHJ0NbFw0xMTAxMjcxNjUzNTZaMAAwIQIOBTKv2ui+KFMI+wcnQ5YXDTEwMDkxNTEwMjE1N1owADAhAg49F3c/GSah+oRUBydDmxcNMTEwMTI3MTczMjMzWjAAMCECDggv4I61WwpKFMMHJ0OcFw0xMTAxMjcxNzMyNTVaMAAwIQIOXx/Y8sEvwS10LAcnQ6UXDTExMDEyODExMjkzN1owADAhAg5LSLbnVrSKaw/9BydDphcNMTEwMTI4MTEyOTIwWjAAMCECDmFFoCuhKUeACQQHJ0PfFw0xMTAxMTExMDE3MzdaMAAwIQIOQTDdFh2fSPF6AAcnQ+AXDTExMDExMTEwMTcxMFowADAhAg5B8AOXX61FpvbbBydD5RcNMTAxMDA2MTAxNDM2WjAAMCECDh41P2Gmi7PkwI4HJ0PmFw0xMDEwMDYxMDE2MjVaMAAwIQIOWUHGLQCd+Ale9gcnQ/0XDTExMDUwMjA3NTYxMFowADAhAg5Z2c9AYkikmgWOBydD/hcNMTEwNTAyMDc1NjM0WjAAMCECDmf/UD+/h8nf+74HJ0QVFw0xMTA0MTUwNzI4MzNaMAAwIQIOICvj4epy3MrqfwcnRBYXDTExMDQxNTA3Mjg1NlowADAhAg4bouRMfOYqgv4xBydEHxcNMTEwMzA4MTYyNDI1WjAAMCECDhebWHGoKiTp7pEHJ0QgFw0xMTAzMDgxNjI0NDhaMAAwIQIOX+qnxxAqJ8LtawcnRDcXDTExMDEzMTE1MTIyOFowADAhAg4j0fICqZ+wkOdqBydEOBcNMTEwMTMxMTUxMTQxWjAAMCECDhmXjsV4SUpWtAMHJ0RLFw0xMTAxMjgxMTI0MTJaMAAwIQIODno/w+zG43kkTwcnREwXDTExMDEyODExMjM1MlowADAhAg4b1gc88767Fr+LBydETxcNMTEwMTI4MTEwMjA4WjAAMCECDn+M3Pa1w2nyFeUHJ0RQFw0xMTAxMjgxMDU4NDVaMAAwIQIOaduoyIH61tqybAcnRJUXDTEwMTIxNTA5NDMyMlowADAhAg4nLqQPkyi3ESAKBydElhcNMTAxMjE1MDk0MzM2WjAAMCECDi504NIMH8578gQHJ0SbFw0xMTAyMTQxNDA1NDFaMAAwIQIOGuaM8PDaC5u1egcnRJwXDTExMDIxNDE0MDYwNFowADAhAg4ehYq/BXGnB5PWBydEnxcNMTEwMjA0MDgwOTUxWjAAMCECDkSD4eS4FxW5H20HJ0SgFw0xMTAyMDQwODA5MjVaMAAwIQIOOCcb6ilYObt1egcnRKEXDTExMDEyNjEwNDEyOVowADAhAg58tISWCCwFnKGnBydEohcNMTEwMjA0MDgxMzQyWjAAMCECDn5rjtabY/L/WL0HJ0TJFw0xMTAyMDQxMTAzNDFaMAAwDQYJKoZIhvcNAQEFBQADggEBAGnF2Gs0+LNiYCW1Ipm83OXQYP/bd5tFFRzyz3iepFqNfYs4D68/QihjFoRHQoXEB0OEe1tvaVnnPGnEOpi6krwekquMxo4H88B5SlyiFIqemCOIss0SxlCFs69LmfRYvPPvPEhoXtQ3ZThe0UvKG83GOklhvGl6OaiRf4Mt+m8zOT4Wox/j6aOBK6cw6qKCdmD+Yj1rrNqFGg1CnSWMoD6S6mwNgkzwdBUJZ22BwrzAAo4RHa2Uy3ef1FjwD0XtU5N3uDSxGGBEDvOe5z82rps3E22FpAA8eYl8kaXtmWqyvYU0epp4brGuTxCuBMCAsxt/OjIjeNNQbBGkwxgfYA0=" + +const pemCRLBase64 = "LS0tLS1CRUdJTiBYNTA5IENSTC0tLS0tDQpNSUlCOWpDQ0FWOENBUUV3RFFZSktvWklodmNOQVFFRkJRQXdiREVhTUJnR0ExVUVDaE1SVWxOQklGTmxZM1Z5DQphWFI1SUVsdVl5NHhIakFjQmdOVkJBTVRGVkpUUVNCUWRXSnNhV01nVW05dmRDQkRRU0IyTVRFdU1Dd0dDU3FHDQpTSWIzRFFFSkFSWWZjbk5oYTJWdmJuSnZiM1J6YVdkdVFISnpZWE5sWTNWeWFYUjVMbU52YlJjTk1URXdNakl6DQpNVGt5T0RNd1doY05NVEV3T0RJeU1Ua3lPRE13V2pDQmpEQktBaEVBckRxb2g5RkhKSFhUN09QZ3V1bjQrQmNODQpNRGt4TVRBeU1UUXlOekE1V2pBbU1Bb0dBMVVkRlFRRENnRUpNQmdHQTFVZEdBUVJHQTh5TURBNU1URXdNakUwDQpNalExTlZvd1BnSVJBTEd6blowOTVQQjVhQU9MUGc1N2ZNTVhEVEF5TVRBeU16RTBOVEF4TkZvd0dqQVlCZ05WDQpIUmdFRVJnUE1qQXdNakV3TWpNeE5EVXdNVFJhb0RBd0xqQWZCZ05WSFNNRUdEQVdnQlQxVERGNlVRTS9MTmVMDQpsNWx2cUhHUXEzZzltekFMQmdOVkhSUUVCQUlDQUlRd0RRWUpLb1pJaHZjTkFRRUZCUUFEZ1lFQUZVNUFzNk16DQpxNVBSc2lmYW9iUVBHaDFhSkx5QytNczVBZ2MwYld5QTNHQWR4dXI1U3BQWmVSV0NCamlQL01FSEJXSkNsQkhQDQpHUmNxNXlJZDNFakRrYUV5eFJhK2k2N0x6dmhJNmMyOUVlNks5cFNZd2ppLzdSVWhtbW5Qclh0VHhsTDBsckxyDQptUVFKNnhoRFJhNUczUUE0Q21VZHNITnZicnpnbUNZcHZWRT0NCi0tLS0tRU5EIFg1MDkgQ1JMLS0tLS0NCg0K" diff --git a/src/pkg/ebnf/ebnf.go b/src/pkg/ebnf/ebnf.go index 964e1c1b0..661afdd35 100644 --- a/src/pkg/ebnf/ebnf.go +++ b/src/pkg/ebnf/ebnf.go @@ -8,7 +8,7 @@ // Production = name "=" [ Expression ] "." . // Expression = Alternative { "|" Alternative } . // Alternative = Term { Term } . -// Term = name | token [ "..." token ] | Group | Option | Repetition . +// Term = name | token [ "…" token ] | Group | Option | Repetition . // Group = "(" Expression ")" . // Option = "[" Expression "]" . // Repetition = "{" Expression "}" . @@ -82,6 +82,12 @@ type ( Body Expression // {body} } + // A Bad node stands for pieces of source code that lead to a parse error. + Bad struct { + TokPos token.Pos + Error string // parser error message + } + // A Production node represents an EBNF production. Production struct { Name *Name @@ -103,6 +109,7 @@ func (x *Range) Pos() token.Pos { return x.Begin.Pos() } func (x *Group) Pos() token.Pos { return x.Lparen } func (x *Option) Pos() token.Pos { return x.Lbrack } func (x *Repetition) Pos() token.Pos { return x.Lbrace } +func (x *Bad) Pos() token.Pos { return x.TokPos } func (x *Production) Pos() token.Pos { return x.Name.Pos() } diff --git a/src/pkg/ebnf/ebnf_test.go b/src/pkg/ebnf/ebnf_test.go index 2055f872a..30301748d 100644 --- a/src/pkg/ebnf/ebnf_test.go +++ b/src/pkg/ebnf/ebnf_test.go @@ -14,7 +14,7 @@ import ( var fset = token.NewFileSet() -var grammars = []string{ +var goodGrammars = []string{ `Program = .`, `Program = foo . @@ -38,7 +38,19 @@ var grammars = []string{ } -func check(t *testing.T, filename string, src []byte) { +var badGrammars = []string{ + `Program = | .`, + `Program = | b .`, + `Program = a … b .`, + `Program = "a" … .`, + `Program = … "b" .`, + `Program = () .`, + `Program = [] .`, + `Program = {} .`, +} + + +func checkGood(t *testing.T, filename string, src []byte) { grammar, err := Parse(fset, filename, src) if err != nil { t.Errorf("Parse(%s) failed: %v", src, err) @@ -49,9 +61,20 @@ func check(t *testing.T, filename string, src []byte) { } +func checkBad(t *testing.T, filename string, src []byte) { + _, err := Parse(fset, filename, src) + if err == nil { + t.Errorf("Parse(%s) should have failed", src) + } +} + + func TestGrammars(t *testing.T) { - for _, src := range grammars { - check(t, "", []byte(src)) + for _, src := range goodGrammars { + checkGood(t, "", []byte(src)) + } + for _, src := range badGrammars { + checkBad(t, "", []byte(src)) } } @@ -67,6 +90,6 @@ func TestFiles(t *testing.T) { if err != nil { t.Fatal(err) } - check(t, filename, src) + checkGood(t, filename, src) } } diff --git a/src/pkg/ebnf/parser.go b/src/pkg/ebnf/parser.go index 166412f99..ede4f7073 100644 --- a/src/pkg/ebnf/parser.go +++ b/src/pkg/ebnf/parser.go @@ -85,6 +85,7 @@ func (p *parser) parseToken() *Token { } +// ParseTerm returns nil if no term was found. func (p *parser) parseTerm() (x Expression) { pos := p.pos @@ -131,7 +132,8 @@ func (p *parser) parseSequence() Expression { // no need for a sequence if list.Len() < 2 switch len(list) { case 0: - return nil + p.errorExpected(p.pos, "term") + return &Bad{p.pos, "term expected"} case 1: return list[0] } @@ -144,20 +146,16 @@ func (p *parser) parseExpression() Expression { var list Alternative for { - if x := p.parseSequence(); x != nil { - list = append(list, x) - } + list = append(list, p.parseSequence()) if p.tok != token.OR { break } p.next() } + // len(list) > 0 // no need for an Alternative node if list.Len() < 2 - switch len(list) { - case 0: - return nil - case 1: + if len(list) == 1 { return list[0] } @@ -168,7 +166,10 @@ func (p *parser) parseExpression() Expression { func (p *parser) parseProduction() *Production { name := p.parseIdentifier() p.expect(token.ASSIGN) - expr := p.parseExpression() + var expr Expression + if p.tok != token.PERIOD { + expr = p.parseExpression() + } p.expect(token.PERIOD) return &Production{name, expr} } diff --git a/src/pkg/exec/exec.go b/src/pkg/exec/exec.go index c6a5e06bb..935f24c21 100644 --- a/src/pkg/exec/exec.go +++ b/src/pkg/exec/exec.go @@ -62,9 +62,11 @@ type Cmd struct { Stdout io.Writer Stderr io.Writer + // Process is the underlying process, once started. + Process *os.Process + err os.Error // last error (from LookPath, stdin, stdout, stderr) - process *os.Process - finished bool // when Wait was called + finished bool // when Wait was called childFiles []*os.File closeAfterStart []io.Closer closeAfterWait []io.Closer @@ -205,7 +207,7 @@ func (c *Cmd) Start() os.Error { if c.err != nil { return c.err } - if c.process != nil { + if c.Process != nil { return os.NewError("exec: already started") } @@ -219,7 +221,7 @@ func (c *Cmd) Start() os.Error { } var err os.Error - c.process, err = os.StartProcess(c.Path, c.argv(), &os.ProcAttr{ + c.Process, err = os.StartProcess(c.Path, c.argv(), &os.ProcAttr{ Dir: c.Dir, Files: c.childFiles, Env: c.envv(), @@ -253,14 +255,14 @@ func (c *Cmd) Start() os.Error { // error is of type *os.Waitmsg. Other error types may be // returned for I/O problems. func (c *Cmd) Wait() os.Error { - if c.process == nil { + if c.Process == nil { return os.NewError("exec: not started") } if c.finished { return os.NewError("exec: Wait was already called") } c.finished = true - msg, err := c.process.Wait(0) + msg, err := c.Process.Wait(0) var copyError os.Error for _ = range c.goroutine { @@ -315,7 +317,7 @@ func (c *Cmd) StdinPipe() (io.WriteCloser, os.Error) { if c.Stdin != nil { return nil, os.NewError("exec: Stdin already set") } - if c.process != nil { + if c.Process != nil { return nil, os.NewError("exec: StdinPipe after process started") } pr, pw, err := os.Pipe() @@ -334,7 +336,7 @@ func (c *Cmd) StdoutPipe() (io.Reader, os.Error) { if c.Stdout != nil { return nil, os.NewError("exec: Stdout already set") } - if c.process != nil { + if c.Process != nil { return nil, os.NewError("exec: StdoutPipe after process started") } pr, pw, err := os.Pipe() @@ -353,7 +355,7 @@ func (c *Cmd) StderrPipe() (io.Reader, os.Error) { if c.Stderr != nil { return nil, os.NewError("exec: Stderr already set") } - if c.process != nil { + if c.Process != nil { return nil, os.NewError("exec: StderrPipe after process started") } pr, pw, err := os.Pipe() diff --git a/src/pkg/exp/gui/Makefile b/src/pkg/exp/gui/Makefile new file mode 100644 index 000000000..af065e4a5 --- /dev/null +++ b/src/pkg/exp/gui/Makefile @@ -0,0 +1,11 @@ +# Copyright 2009 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. + +include ../../../Make.inc + +TARG=exp/gui +GOFILES=\ + gui.go\ + +include ../../../Make.pkg diff --git a/src/pkg/exp/draw/event.go b/src/pkg/exp/gui/gui.go index b777d912e..171499186 100644 --- a/src/pkg/exp/draw/event.go +++ b/src/pkg/exp/gui/gui.go @@ -2,17 +2,19 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package draw +// Package gui defines a basic graphical user interface programming model. +package gui import ( "image" + "image/draw" "os" ) // A Window represents a single graphics window. type Window interface { // Screen returns an editable Image for the window. - Screen() Image + Screen() draw.Image // FlushImage flushes changes made to Screen() back to screen. FlushImage() // EventChan returns a channel carrying UI events such as key presses, diff --git a/src/pkg/exp/draw/x11/Makefile b/src/pkg/exp/gui/x11/Makefile index 205b3a65b..88cc1e23b 100644 --- a/src/pkg/exp/draw/x11/Makefile +++ b/src/pkg/exp/gui/x11/Makefile @@ -4,7 +4,7 @@ include ../../../../Make.inc -TARG=exp/draw/x11 +TARG=exp/gui/x11 GOFILES=\ auth.go\ conn.go\ diff --git a/src/pkg/exp/draw/x11/auth.go b/src/pkg/exp/gui/x11/auth.go index d48936ac1..d48936ac1 100644 --- a/src/pkg/exp/draw/x11/auth.go +++ b/src/pkg/exp/gui/x11/auth.go diff --git a/src/pkg/exp/draw/x11/conn.go b/src/pkg/exp/gui/x11/conn.go index 23edc2c63..bc7ca63db 100644 --- a/src/pkg/exp/draw/x11/conn.go +++ b/src/pkg/exp/gui/x11/conn.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package x11 implements an X11 backend for the exp/draw package. +// Package x11 implements an X11 backend for the exp/gui package. // // The X protocol specification is at ftp://ftp.x.org/pub/X11R7.0/doc/PDF/proto.pdf. // A summary of the wire format can be found in XCB's xproto.xml. @@ -10,8 +10,9 @@ package x11 import ( "bufio" - "exp/draw" + "exp/gui" "image" + "image/draw" "io" "log" "net" @@ -43,7 +44,7 @@ type conn struct { img *image.RGBA eventc chan interface{} - mouseState draw.MouseEvent + mouseState gui.MouseEvent buf [256]byte // General purpose scratch buffer. @@ -53,7 +54,7 @@ type conn struct { } // writeSocket runs in its own goroutine, serving both FlushImage calls -// directly from the exp/draw client and indirectly from X expose events. +// directly from the exp/gui client and indirectly from X expose events. // It paints c.img to the X server via PutImage requests. func (c *conn) writeSocket() { defer c.c.Close() @@ -143,7 +144,7 @@ func (c *conn) Close() os.Error { func (c *conn) EventChan() <-chan interface{} { return c.eventc } -// readSocket runs in its own goroutine, reading X events and sending draw +// readSocket runs in its own goroutine, reading X events and sending gui // events on c's EventChan. func (c *conn) readSocket() { var ( @@ -155,7 +156,7 @@ func (c *conn) readSocket() { // X events are always 32 bytes long. if _, err := io.ReadFull(c.r, c.buf[0:32]); err != nil { if err != os.EOF { - c.eventc <- draw.ErrEvent{err} + c.eventc <- gui.ErrEvent{err} } return } @@ -165,7 +166,7 @@ func (c *conn) readSocket() { if cookie != 1 { // We issued only one request (GetKeyboardMapping) with a cookie of 1, // so we shouldn't get any other reply from the X server. - c.eventc <- draw.ErrEvent{os.NewError("x11: unexpected cookie")} + c.eventc <- gui.ErrEvent{os.NewError("x11: unexpected cookie")} return } keysymsPerKeycode = int(c.buf[1]) @@ -179,7 +180,7 @@ func (c *conn) readSocket() { u, err := readU32LE(c.r, c.buf[0:4]) if err != nil { if err != os.EOF { - c.eventc <- draw.ErrEvent{err} + c.eventc <- gui.ErrEvent{err} } return } @@ -204,11 +205,11 @@ func (c *conn) readSocket() { // TODO(nigeltao): Should we send KeyEvents for Shift/Ctrl/Alt? Should Shift-A send // the same int down the channel as the sent on just the A key? // TODO(nigeltao): How should IME events (e.g. key presses that should generate CJK text) work? Or - // is that outside the scope of the draw.Window interface? + // is that outside the scope of the gui.Window interface? if c.buf[0] == 0x03 { keysym = -keysym } - c.eventc <- draw.KeyEvent{keysym} + c.eventc <- gui.KeyEvent{keysym} case 0x04, 0x05: // Button press, button release. mask := 1 << (c.buf[1] - 1) if c.buf[0] == 0x04 { @@ -551,7 +552,7 @@ func (c *conn) handshake() os.Error { } // NewWindow calls NewWindowDisplay with $DISPLAY. -func NewWindow() (draw.Window, os.Error) { +func NewWindow() (gui.Window, os.Error) { display := os.Getenv("DISPLAY") if len(display) == 0 { return nil, os.NewError("$DISPLAY not set") @@ -559,10 +560,10 @@ func NewWindow() (draw.Window, os.Error) { return NewWindowDisplay(display) } -// NewWindowDisplay returns a new draw.Window, backed by a newly created and +// NewWindowDisplay returns a new gui.Window, backed by a newly created and // mapped X11 window. The X server to connect to is specified by the display // string, such as ":1". -func NewWindowDisplay(display string) (draw.Window, os.Error) { +func NewWindowDisplay(display string) (gui.Window, os.Error) { socket, displayStr, err := connect(display) if err != nil { return nil, err diff --git a/src/pkg/fmt/fmt_test.go b/src/pkg/fmt/fmt_test.go index caecb6fb8..122b9516b 100644 --- a/src/pkg/fmt/fmt_test.go +++ b/src/pkg/fmt/fmt_test.go @@ -132,15 +132,15 @@ var fmttests = []struct { {"%q", `"`, `"\""`}, {"%q", "\a\b\f\r\n\t\v", `"\a\b\f\r\n\t\v"`}, {"%q", "abc\xffdef", `"abc\xffdef"`}, - {"%q", "\u263a", `"\u263a"`}, + {"%q", "\u263a", `"☺"`}, {"%q", "\U0010ffff", `"\U0010ffff"`}, // escaped characters {"%q", 'x', `'x'`}, {"%q", 0, `'\x00'`}, {"%q", '\n', `'\n'`}, - {"%q", '\u1234', `'\u1234'`}, - {"%q", '\U00012345', `'\U00012345'`}, + {"%q", '\u0e00', `'\u0e00'`}, // not a printable rune. + {"%q", '\U000c2345', `'\U000c2345'`}, // not a printable rune. {"%q", int64(0x7FFFFFFF), `%!q(int64=2147483647)`}, {"%q", uint64(0xFFFFFFFF), `%!q(uint64=4294967295)`}, {"%q", '"', `'"'`}, @@ -148,7 +148,7 @@ var fmttests = []struct { // width {"%5s", "abc", " abc"}, - {"%2s", "\u263a", " \u263a"}, + {"%2s", "\u263a", " ☺"}, {"%-5s", "abc", "abc "}, {"%-8q", "abc", `"abc" `}, {"%05s", "abc", "00abc"}, @@ -158,9 +158,9 @@ var fmttests = []struct { {"%.5s", "日本語日本語", "日本語日本"}, {"%.5s", []byte("日本語日本語"), "日本語日本"}, {"%.5q", "abcdefghijklmnopqrstuvwxyz", `"abcde"`}, - {"%.3q", "日本語日本語", `"\u65e5\u672c\u8a9e"`}, - {"%.3q", []byte("日本語日本語"), `"\u65e5\u672c\u8a9e"`}, - {"%10.1q", "日本語日本語", ` "\u65e5"`}, + {"%.3q", "日本語日本語", `"日本語"`}, + {"%.3q", []byte("日本語日本語"), `"日本語"`}, + {"%10.1q", "日本語日本語", ` "日"`}, // integers {"%d", 12345, "12345"}, diff --git a/src/pkg/go/build/Makefile b/src/pkg/go/build/Makefile new file mode 100644 index 000000000..4411940ae --- /dev/null +++ b/src/pkg/go/build/Makefile @@ -0,0 +1,22 @@ +# Copyright 2009 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. + +include ../../../Make.inc + +TARG=go/build +GOFILES=\ + build.go\ + dir.go\ + path.go\ + syslist.go\ + +CLEANFILES+=syslist.go + +include ../../../Make.pkg + +syslist.go: ../../../Make.inc Makefile + echo '// Generated automatically by make.' >$@ + echo 'package build' >>$@ + echo 'const goosList = "$(GOOS_LIST)"' >>$@ + echo 'const goarchList = "$(GOARCH_LIST)"' >>$@ diff --git a/src/pkg/go/build/build.go b/src/pkg/go/build/build.go new file mode 100644 index 000000000..3cb8efe47 --- /dev/null +++ b/src/pkg/go/build/build.go @@ -0,0 +1,272 @@ +// Copyright 2011 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 build provides tools for building Go packages. +package build + +import ( + "bytes" + "exec" + "fmt" + "os" + "path/filepath" + "runtime" + "strings" +) + +func (d *DirInfo) Build(targ string) ([]*Cmd, os.Error) { + b := &build{obj: "_obj/"} + + goarch := runtime.GOARCH + if g := os.Getenv("GOARCH"); g != "" { + goarch = g + } + var err os.Error + b.arch, err = ArchChar(goarch) + if err != nil { + return nil, err + } + + var gofiles = d.GoFiles // .go files to be built with gc + var ofiles []string // *.GOARCH files to be linked or packed + + // make build directory + b.mkdir(b.obj) + + // cgo + if len(d.CgoFiles) > 0 { + outGo, outObj := b.cgo(d.CgoFiles) + gofiles = append(gofiles, outGo...) + ofiles = append(ofiles, outObj...) + } + + // compile + if len(gofiles) > 0 { + ofile := b.obj + "_go_." + b.arch + b.gc(ofile, gofiles...) + ofiles = append(ofiles, ofile) + } + + // assemble + for _, sfile := range d.SFiles { + ofile := b.obj + sfile[:len(sfile)-1] + b.arch + b.asm(ofile, sfile) + ofiles = append(ofiles, ofile) + } + + if len(ofiles) == 0 { + return nil, os.NewError("make: no object files to build") + } + + if d.IsCommand() { + b.ld(targ, ofiles...) + } else { + b.gopack(targ, ofiles...) + } + + return b.cmds, nil +} + +type Cmd struct { + Args []string // command-line + Stdout string // write standard output to this file, "" is passthrough + Input []string // file paths (dependencies) + Output []string // file paths +} + +func (c *Cmd) String() string { + return strings.Join(c.Args, " ") +} + +func (c *Cmd) Run(dir string) os.Error { + out := new(bytes.Buffer) + cmd := exec.Command(c.Args[0], c.Args[1:]...) + cmd.Dir = dir + cmd.Stdout = out + cmd.Stderr = out + if c.Stdout != "" { + f, err := os.Create(filepath.Join(dir, c.Stdout)) + if err != nil { + return err + } + defer f.Close() + cmd.Stdout = f + } + if err := cmd.Run(); err != nil { + return fmt.Errorf("command %q: %v\n%v", c, err, out) + } + return nil +} + +func (c *Cmd) Clean(dir string) (err os.Error) { + for _, fn := range c.Output { + if e := os.RemoveAll(fn); err == nil { + err = e + } + } + return +} + +// ArchChar returns the architecture character for the given goarch. +// For example, ArchChar("amd64") returns "6". +func ArchChar(goarch string) (string, os.Error) { + switch goarch { + case "386": + return "8", nil + case "amd64": + return "6", nil + case "arm": + return "5", nil + } + return "", os.NewError("unsupported GOARCH " + goarch) +} + +type build struct { + cmds []*Cmd + obj string + arch string +} + +func (b *build) add(c Cmd) { + b.cmds = append(b.cmds, &c) +} + +func (b *build) mkdir(name string) { + b.add(Cmd{ + Args: []string{"mkdir", "-p", name}, + Output: []string{name}, + }) +} + +func (b *build) gc(ofile string, gofiles ...string) { + gc := b.arch + "g" + args := append([]string{gc, "-o", ofile}, gcImportArgs...) + args = append(args, gofiles...) + b.add(Cmd{ + Args: args, + Input: gofiles, + Output: []string{ofile}, + }) +} + +func (b *build) asm(ofile string, sfile string) { + asm := b.arch + "a" + b.add(Cmd{ + Args: []string{asm, "-o", ofile, sfile}, + Input: []string{sfile}, + Output: []string{ofile}, + }) +} + +func (b *build) ld(targ string, ofiles ...string) { + ld := b.arch + "l" + args := append([]string{ld, "-o", targ}, ldImportArgs...) + args = append(args, ofiles...) + b.add(Cmd{ + Args: args, + Input: ofiles, + Output: []string{targ}, + }) +} + +func (b *build) gopack(targ string, ofiles ...string) { + b.add(Cmd{ + Args: append([]string{"gopack", "grc", targ}, ofiles...), + Input: ofiles, + Output: []string{targ}, + }) +} + +func (b *build) cc(ofile string, cfiles ...string) { + cc := b.arch + "c" + dir := fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH) + inc := filepath.Join(runtime.GOROOT(), "pkg", dir) + args := []string{cc, "-FVw", "-I", inc, "-o", ofile} + b.add(Cmd{ + Args: append(args, cfiles...), + Input: cfiles, + Output: []string{ofile}, + }) +} + +func (b *build) gccCompile(ofile, cfile string) { + b.add(Cmd{ + Args: gccArgs(b.arch, "-o", ofile, "-c", cfile), + Input: []string{cfile}, + Output: []string{ofile}, + }) +} + +func (b *build) gccLink(ofile string, ofiles ...string) { + b.add(Cmd{ + Args: append(gccArgs(b.arch, "-o", ofile), ofiles...), + Input: ofiles, + Output: []string{ofile}, + }) +} + +func gccArgs(arch string, args ...string) []string { + // TODO(adg): HOST_CC + m := "-m32" + if arch == "6" { + m = "-m64" + } + return append([]string{"gcc", m, "-I", ".", "-g", "-fPIC", "-O2"}, args...) +} + +func (b *build) cgo(cgofiles []string) (outGo, outObj []string) { + // cgo + // TODO(adg): CGOPKGPATH + // TODO(adg): CGO_FLAGS + gofiles := []string{b.obj + "_cgo_gotypes.go"} + cfiles := []string{b.obj + "_cgo_main.c", b.obj + "_cgo_export.c"} + for _, fn := range cgofiles { + f := b.obj + fn[:len(fn)-2] + gofiles = append(gofiles, f+"cgo1.go") + cfiles = append(cfiles, f+"cgo2.c") + } + defunC := b.obj + "_cgo_defun.c" + output := append([]string{defunC}, gofiles...) + output = append(output, cfiles...) + b.add(Cmd{ + Args: append([]string{"cgo", "--"}, cgofiles...), + Input: cgofiles, + Output: output, + }) + outGo = append(outGo, gofiles...) + + // cc _cgo_defun.c + defunObj := b.obj + "_cgo_defun." + b.arch + b.cc(defunObj, defunC) + outObj = append(outObj, defunObj) + + // gcc + linkobj := make([]string, 0, len(cfiles)) + for _, cfile := range cfiles { + ofile := cfile[:len(cfile)-1] + "o" + b.gccCompile(ofile, cfile) + linkobj = append(linkobj, ofile) + if !strings.HasSuffix(ofile, "_cgo_main.o") { + outObj = append(outObj, ofile) + } + } + dynObj := b.obj + "_cgo1_.o" + b.gccLink(dynObj, linkobj...) + + // cgo -dynimport + importC := b.obj + "_cgo_import.c" + b.add(Cmd{ + Args: []string{"cgo", "-dynimport", dynObj}, + Stdout: importC, + Input: []string{dynObj}, + Output: []string{importC}, + }) + + // cc _cgo_import.ARCH + importObj := b.obj + "_cgo_import." + b.arch + b.cc(importObj, importC) + outObj = append(outObj, importObj) + + return +} diff --git a/src/pkg/go/build/build_test.go b/src/pkg/go/build/build_test.go new file mode 100644 index 000000000..c543eddbd --- /dev/null +++ b/src/pkg/go/build/build_test.go @@ -0,0 +1,57 @@ +// Copyright 2011 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 build + +import ( + "os" + "path/filepath" + "runtime" + "strings" + "testing" +) + +var buildDirs = []string{ + "pkg/path", + "cmd/gofix", + "pkg/big", + "pkg/go/build/cgotest", +} + +func TestBuild(t *testing.T) { + out, err := filepath.Abs("_test/out") + if err != nil { + t.Fatal(err) + } + for _, d := range buildDirs { + if runtime.GOARCH == "arm" && strings.Contains(d, "/cgo") { + // no cgo for arm, yet. + continue + } + dir := filepath.Join(runtime.GOROOT(), "src", d) + testBuild(t, dir, out) + } +} + +func testBuild(t *testing.T, dir, targ string) { + d, err := ScanDir(dir, true) + if err != nil { + t.Error(err) + return + } + defer os.Remove(targ) + cmds, err := d.Build(targ) + if err != nil { + t.Error(err) + return + } + for _, c := range cmds { + t.Log("Run:", c) + err = c.Run(dir) + if err != nil { + t.Error(c, err) + return + } + } +} diff --git a/src/pkg/go/build/cgotest/file.go b/src/pkg/go/build/cgotest/file.go new file mode 100644 index 000000000..3b2a2e7d9 --- /dev/null +++ b/src/pkg/go/build/cgotest/file.go @@ -0,0 +1,45 @@ +// Copyright 2009 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. + +/* +A trivial example of wrapping a C library in Go. +For a more complex example and explanation, +see ../gmp/gmp.go. +*/ + +package stdio + +/* +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <errno.h> + +char* greeting = "hello, world"; +*/ +import "C" +import "unsafe" + +type File C.FILE + +// TODO(brainman): uncomment once stdout and stderr references are working on Windows. +//var Stdout = (*File)(C.stdout) +//var Stderr = (*File)(C.stderr) + +// Test reference to library symbol. +// Stdout and stderr are too special to be a reliable test. +var myerr = C.sys_errlist + +func (f *File) WriteString(s string) { + p := C.CString(s) + C.fputs(p, (*C.FILE)(f)) + C.free(unsafe.Pointer(p)) + f.Flush() +} + +func (f *File) Flush() { + C.fflush((*C.FILE)(f)) +} + +var Greeting = C.GoString(C.greeting) diff --git a/src/pkg/go/build/dir.go b/src/pkg/go/build/dir.go new file mode 100644 index 000000000..77e80bff0 --- /dev/null +++ b/src/pkg/go/build/dir.go @@ -0,0 +1,173 @@ +// Copyright 2011 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 build + +import ( + "go/parser" + "go/token" + "log" + "os" + "path/filepath" + "strconv" + "strings" + "runtime" +) + +type DirInfo struct { + GoFiles []string // .go files in dir (excluding CgoFiles) + CgoFiles []string // .go files that import "C" + CFiles []string // .c files in dir + SFiles []string // .s files in dir + Imports []string // All packages imported by goFiles + PkgName string // Name of package in dir +} + +func (d *DirInfo) IsCommand() bool { + return d.PkgName == "main" +} + +// ScanDir returns a structure with details about the Go content found +// in the given directory. The file lists exclude: +// +// - files in package main (unless allowMain is true) +// - files in package documentation +// - files ending in _test.go +// - files starting with _ or . +// +// Only files that satisfy the goodOSArch function are included. +func ScanDir(dir string, allowMain bool) (info *DirInfo, err os.Error) { + f, err := os.Open(dir) + if err != nil { + return nil, err + } + dirs, err := f.Readdir(-1) + f.Close() + if err != nil { + return nil, err + } + + var di DirInfo + imported := make(map[string]bool) + pkgName := "" + fset := token.NewFileSet() + for i := range dirs { + d := &dirs[i] + if strings.HasPrefix(d.Name, "_") || + strings.HasPrefix(d.Name, ".") { + continue + } + if !goodOSArch(d.Name) { + continue + } + + switch filepath.Ext(d.Name) { + case ".go": + if strings.HasSuffix(d.Name, "_test.go") { + continue + } + case ".c": + di.CFiles = append(di.CFiles, d.Name) + continue + case ".s": + di.SFiles = append(di.SFiles, d.Name) + continue + default: + continue + } + + filename := filepath.Join(dir, d.Name) + pf, err := parser.ParseFile(fset, filename, nil, parser.ImportsOnly) + if err != nil { + return nil, err + } + s := string(pf.Name.Name) + if s == "main" && !allowMain { + continue + } + if s == "documentation" { + continue + } + if pkgName == "" { + pkgName = s + } else if pkgName != s { + // Only if all files in the directory are in package main + // do we return pkgName=="main". + // A mix of main and another package reverts + // to the original (allowMain=false) behaviour. + if s == "main" || pkgName == "main" { + return ScanDir(dir, false) + } + return nil, os.ErrorString("multiple package names in " + dir) + } + isCgo := false + for _, spec := range pf.Imports { + quoted := string(spec.Path.Value) + path, err := strconv.Unquote(quoted) + if err != nil { + log.Panicf("%s: parser returned invalid quoted string: <%s>", filename, quoted) + } + imported[path] = true + if path == "C" { + isCgo = true + } + } + if isCgo { + di.CgoFiles = append(di.CgoFiles, d.Name) + } else { + di.GoFiles = append(di.GoFiles, d.Name) + } + } + di.Imports = make([]string, len(imported)) + i := 0 + for p := range imported { + di.Imports[i] = p + i++ + } + return &di, nil +} + +// goodOSArch returns false if the filename contains a $GOOS or $GOARCH +// suffix which does not match the current system. +// The recognized filename formats are: +// +// name_$(GOOS).* +// name_$(GOARCH).* +// name_$(GOOS)_$(GOARCH).* +// +func goodOSArch(filename string) bool { + if dot := strings.Index(filename, "."); dot != -1 { + filename = filename[:dot] + } + l := strings.Split(filename, "_", -1) + n := len(l) + if n == 0 { + return true + } + if good, known := goodOS[l[n-1]]; known { + return good + } + if good, known := goodArch[l[n-1]]; known { + if !good || n < 2 { + return false + } + good, known = goodOS[l[n-2]] + return good || !known + } + return true +} + +var goodOS = make(map[string]bool) +var goodArch = make(map[string]bool) + +func init() { + goodOS = make(map[string]bool) + goodArch = make(map[string]bool) + for _, v := range strings.Fields(goosList) { + goodOS[v] = v == runtime.GOOS + } + for _, v := range strings.Fields(goarchList) { + goodArch[v] = v == runtime.GOARCH + } +} diff --git a/src/pkg/go/build/path.go b/src/pkg/go/build/path.go new file mode 100644 index 000000000..8ad39fb0f --- /dev/null +++ b/src/pkg/go/build/path.go @@ -0,0 +1,163 @@ +// Copyright 2011 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 build + +import ( + "fmt" + "log" + "os" + "path/filepath" + "runtime" + "strings" +) + +// Path is a validated list of Trees derived from $GOPATH at init. +var Path []*Tree + +// Tree describes a Go source tree, either $GOROOT or one from $GOPATH. +type Tree struct { + Path string + Goroot bool +} + +func newTree(p string) (*Tree, os.Error) { + if !filepath.IsAbs(p) { + return nil, os.NewError("must be absolute") + } + ep, err := filepath.EvalSymlinks(p) + if err != nil { + return nil, err + } + return &Tree{Path: ep}, nil +} + +// SrcDir returns the tree's package source directory. +func (t *Tree) SrcDir() string { + if t.Goroot { + return filepath.Join(t.Path, "src", "pkg") + } + return filepath.Join(t.Path, "src") +} + +// PkgDir returns the tree's package object directory. +func (t *Tree) PkgDir() string { + goos, goarch := runtime.GOOS, runtime.GOARCH + if e := os.Getenv("GOOS"); e != "" { + goos = e + } + if e := os.Getenv("GOARCH"); e != "" { + goarch = e + } + return filepath.Join(t.Path, "pkg", goos+"_"+goarch) +} + +// BinDir returns the tree's binary executable directory. +func (t *Tree) BinDir() string { + return filepath.Join(t.Path, "bin") +} + +// HasSrc returns whether the given package's +// source can be found inside this Tree. +func (t *Tree) HasSrc(pkg string) bool { + fi, err := os.Stat(filepath.Join(t.SrcDir(), pkg)) + if err != nil { + return false + } + return fi.IsDirectory() +} + +// HasPkg returns whether the given package's +// object file can be found inside this Tree. +func (t *Tree) HasPkg(pkg string) bool { + fi, err := os.Stat(filepath.Join(t.PkgDir(), pkg+".a")) + if err != nil { + return false + } + return fi.IsRegular() + // TODO(adg): check object version is consistent +} + +var ErrNotFound = os.NewError("package could not be found locally") + +// FindTree takes an import or filesystem path and returns the +// tree where the package source should be and the package import path. +func FindTree(path string) (tree *Tree, pkg string, err os.Error) { + if isLocalPath(path) { + if path, err = filepath.Abs(path); err != nil { + return + } + for _, t := range Path { + tpath := t.SrcDir() + string(filepath.Separator) + if !strings.HasPrefix(path, tpath) { + continue + } + tree = t + pkg = path[len(tpath):] + return + } + err = fmt.Errorf("path %q not inside a GOPATH", path) + return + } + tree = defaultTree + pkg = path + for _, t := range Path { + if t.HasSrc(pkg) { + tree = t + return + } + } + err = ErrNotFound + return +} + +// isLocalPath returns whether the given path is local (/foo ./foo ../foo . ..) +func isLocalPath(s string) bool { + const sep = string(filepath.Separator) + return strings.HasPrefix(s, sep) || strings.HasPrefix(s, "."+sep) || strings.HasPrefix(s, ".."+sep) || s == "." || s == ".." +} + +var ( + // argument lists used by the build's gc and ld methods + gcImportArgs []string + ldImportArgs []string + + // default tree for remote packages + defaultTree *Tree +) + +// set up Path: parse and validate GOROOT and GOPATH variables +func init() { + root := runtime.GOROOT() + p, err := newTree(root) + if err != nil { + log.Fatalf("Invalid GOROOT %q: %v", root, err) + } + p.Goroot = true + Path = []*Tree{p} + + for _, p := range filepath.SplitList(os.Getenv("GOPATH")) { + if p == "" { + continue + } + t, err := newTree(p) + if err != nil { + log.Printf("Invalid GOPATH %q: %v", p, err) + continue + } + Path = append(Path, t) + gcImportArgs = append(gcImportArgs, "-I", t.PkgDir()) + ldImportArgs = append(ldImportArgs, "-L", t.PkgDir()) + + // select first GOPATH entry as default + if defaultTree == nil { + defaultTree = t + } + } + + // use GOROOT if no valid GOPATH specified + if defaultTree == nil { + defaultTree = Path[0] + } +} diff --git a/src/pkg/go/build/syslist_test.go b/src/pkg/go/build/syslist_test.go new file mode 100644 index 000000000..eb0e5dcb6 --- /dev/null +++ b/src/pkg/go/build/syslist_test.go @@ -0,0 +1,62 @@ +// Copyright 2011 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 build + +import ( + "runtime" + "testing" +) + +var ( + thisOS = runtime.GOOS + thisArch = runtime.GOARCH + otherOS = anotherOS() + otherArch = anotherArch() +) + +func anotherOS() string { + if thisOS != "darwin" { + return "darwin" + } + return "linux" +} + +func anotherArch() string { + if thisArch != "amd64" { + return "amd64" + } + return "386" +} + +type GoodFileTest struct { + name string + result bool +} + +var tests = []GoodFileTest{ + {"file.go", true}, + {"file.c", true}, + {"file_foo.go", true}, + {"file_" + thisArch + ".go", true}, + {"file_" + otherArch + ".go", false}, + {"file_" + thisOS + ".go", true}, + {"file_" + otherOS + ".go", false}, + {"file_" + thisOS + "_" + thisArch + ".go", true}, + {"file_" + otherOS + "_" + thisArch + ".go", false}, + {"file_" + thisOS + "_" + otherArch + ".go", false}, + {"file_" + otherOS + "_" + otherArch + ".go", false}, + {"file_foo_" + thisArch + ".go", true}, + {"file_foo_" + otherArch + ".go", false}, + {"file_" + thisOS + ".c", true}, + {"file_" + otherOS + ".c", false}, +} + +func TestGoodOSArch(t *testing.T) { + for _, test := range tests { + if goodOSArch(test.name) != test.result { + t.Fatalf("goodOSArch(%q) != %v", test.name, test.result) + } + } +} diff --git a/src/pkg/go/scanner/scanner_test.go b/src/pkg/go/scanner/scanner_test.go index 8af972838..ee1e830a1 100644 --- a/src/pkg/go/scanner/scanner_test.go +++ b/src/pkg/go/scanner/scanner_test.go @@ -652,7 +652,7 @@ var errors = []struct { }{ {"\a", token.ILLEGAL, 0, "illegal character '\\a'"}, {`#`, token.ILLEGAL, 0, "illegal character '#'"}, - {`…`, token.ILLEGAL, 0, "illegal character '\\u2026'"}, + {`…`, token.ILLEGAL, 0, "illegal character '…'"}, {`' '`, token.CHAR, 0, ""}, {`''`, token.CHAR, 0, "illegal character literal"}, {`'\8'`, token.CHAR, 2, "unknown escape sequence"}, diff --git a/src/pkg/go/types/gcimporter.go b/src/pkg/go/types/gcimporter.go index 377c45ad6..2cfed7726 100644 --- a/src/pkg/go/types/gcimporter.go +++ b/src/pkg/go/types/gcimporter.go @@ -403,7 +403,7 @@ func (p *gcParser) parseStructType() Type { } -// Parameter = ( identifier | "?" ) [ "..." ] Type . +// Parameter = ( identifier | "?" ) [ "..." ] Type [ ":" string_lit ] . // func (p *gcParser) parseParameter() (par *ast.Object, isVariadic bool) { name := p.parseName() @@ -415,6 +415,11 @@ func (p *gcParser) parseParameter() (par *ast.Object, isVariadic bool) { isVariadic = true } ptyp := p.parseType() + // ignore argument tag + if p.tok == ':' { + p.next() + p.expect(scanner.String) + } par = ast.NewObj(ast.Var, name) par.Type = ptyp return diff --git a/src/pkg/html/doc.go b/src/pkg/html/doc.go index 55135c3d0..5bc063086 100644 --- a/src/pkg/html/doc.go +++ b/src/pkg/html/doc.go @@ -4,6 +4,7 @@ /* Package html implements an HTML5-compliant tokenizer and parser. +INCOMPLETE. Tokenization is done by creating a Tokenizer for an io.Reader r. It is the caller's responsibility to ensure that r provides UTF-8 encoded HTML. diff --git a/src/pkg/html/token.go b/src/pkg/html/token.go index 6d8eb604e..23c95ece6 100644 --- a/src/pkg/html/token.go +++ b/src/pkg/html/token.go @@ -355,6 +355,33 @@ loop: return z.buf[i0:i], z.trim(i) } +// attrName finds the largest attribute name at the start +// of z.buf[i:] and returns it lower-cased, as well +// as the trimmed cursor location after that word. +// +// http://dev.w3.org/html5/spec/Overview.html#syntax-attribute-name +// TODO: unicode characters +func (z *Tokenizer) attrName(i int) ([]byte, int) { + i0 := i +loop: + for ; i < z.p1; i++ { + c := z.buf[i] + switch c { + case '<', '>', '"', '\'', '/', '=': + break loop + } + switch { + case 'A' <= c && c <= 'Z': + z.buf[i] = c + 'a' - 'A' + case c > ' ' && c < 0x7f: + // No-op. + default: + break loop + } + } + return z.buf[i0:i], z.trim(i) +} + // Text returns the unescaped text of a TextToken or a CommentToken. // The contents of the returned slice may change on the next call to Next. func (z *Tokenizer) Text() []byte { @@ -399,7 +426,7 @@ func (z *Tokenizer) TagName() (name []byte, hasAttr bool) { // attribute for the current tag token and whether there are more attributes. // The contents of the returned slices may change on the next call to Next. func (z *Tokenizer) TagAttr() (key, val []byte, moreAttr bool) { - key, i := z.word(z.p0, true) + key, i := z.attrName(z.p0) // Check for an empty attribute value. if i == z.p1 { z.p0 = i diff --git a/src/pkg/html/token_test.go b/src/pkg/html/token_test.go index 6291af600..c17b436aa 100644 --- a/src/pkg/html/token_test.go +++ b/src/pkg/html/token_test.go @@ -126,6 +126,11 @@ var tokenTests = []tokenTest{ `<input value="yes" foo="BAR">`, }, { + "Unquoted attribute value, spaces", + `<input value = yes FOO = BAR>`, + `<input value="yes" foo="BAR">`, + }, + { "Unquoted attribute value, trailing space", `<input value=yes FOO=BAR >`, `<input value="yes" foo="BAR">`, @@ -145,6 +150,11 @@ var tokenTests = []tokenTest{ `<input value="I'm an attribute" FOO="BAR">`, `<input value="I'm an attribute" foo="BAR">`, }, + { + "Attribute name characters", + `<meta http-equiv="content-type">`, + `<meta http-equiv="content-type">`, + }, } func TestTokenizer(t *testing.T) { diff --git a/src/pkg/http/client.go b/src/pkg/http/client.go index 7e1d65df3..71b037042 100644 --- a/src/pkg/http/client.go +++ b/src/pkg/http/client.go @@ -7,7 +7,6 @@ package http import ( - "bytes" "encoding/base64" "fmt" "io" @@ -240,7 +239,7 @@ func (c *Client) Post(url string, bodyType string, body io.Reader) (r *Response, // Caller should close r.Body when done reading from it. // // PostForm is a wrapper around DefaultClient.PostForm -func PostForm(url string, data map[string]string) (r *Response, err os.Error) { +func PostForm(url string, data Values) (r *Response, err os.Error) { return DefaultClient.PostForm(url, data) } @@ -248,17 +247,8 @@ func PostForm(url string, data map[string]string) (r *Response, err os.Error) { // with data's keys and values urlencoded as the request body. // // Caller should close r.Body when done reading from it. -func (c *Client) PostForm(url string, data map[string]string) (r *Response, err os.Error) { - return c.Post(url, "application/x-www-form-urlencoded", urlencode(data)) -} - -// TODO: remove this function when PostForm takes a multimap. -func urlencode(data map[string]string) (b *bytes.Buffer) { - m := make(map[string][]string, len(data)) - for k, v := range data { - m[k] = []string{v} - } - return bytes.NewBuffer([]byte(EncodeQuery(m))) +func (c *Client) PostForm(url string, data Values) (r *Response, err os.Error) { + return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode())) } // Head issues a HEAD to the specified URL. If the response is one of the diff --git a/src/pkg/http/client_test.go b/src/pkg/http/client_test.go index 822a8889c..9ef81d9d4 100644 --- a/src/pkg/http/client_test.go +++ b/src/pkg/http/client_test.go @@ -109,7 +109,10 @@ func TestPostFormRequestFormat(t *testing.T) { client := &Client{Transport: tr} url := "http://dummy.faketld/" - form := map[string]string{"foo": "bar"} + form := make(Values) + form.Set("foo", "bar") + form.Add("foo", "bar2") + form.Set("bar", "baz") client.PostForm(url, form) // Note: doesn't hit network if tr.req.Method != "POST" { @@ -127,10 +130,17 @@ func TestPostFormRequestFormat(t *testing.T) { if tr.req.Close { t.Error("got Close true, want false") } - if g, e := tr.req.ContentLength, int64(len("foo=bar")); g != e { + expectedBody := "foo=bar&foo=bar2&bar=baz" + if g, e := tr.req.ContentLength, int64(len(expectedBody)); g != e { t.Errorf("got ContentLength %d, want %d", g, e) } - + bodyb, err := ioutil.ReadAll(tr.req.Body) + if err != nil { + t.Fatalf("ReadAll on req.Body: %v", err) + } + if g := string(bodyb); g != expectedBody { + t.Errorf("got body %q, want %q", g, expectedBody) + } } func TestRedirects(t *testing.T) { diff --git a/src/pkg/http/httptest/server.go b/src/pkg/http/httptest/server.go index 8e385d045..879f04f33 100644 --- a/src/pkg/http/httptest/server.go +++ b/src/pkg/http/httptest/server.go @@ -108,29 +108,24 @@ func (s *Server) CloseClientConnections() { // "127.0.0.1" and "[::1]", expiring at the last second of 2049 (the end // of ASN.1 time). var localhostCert = []byte(`-----BEGIN CERTIFICATE----- -MIIBwTCCASugAwIBAgIBADALBgkqhkiG9w0BAQUwADAeFw0xMTAzMzEyMDI1MDda -Fw00OTEyMzEyMzU5NTlaMAAwggCdMAsGCSqGSIb3DQEBAQOCAIwAMIIAhwKCAIB6 -oy4iT42G6qk+GGn5VL5JlnJT6ZG5cqaMNFaNGlIxNb6CPUZLKq2sM3gRaimsktIw -nNAcNwQGHpe1tZo+J/Pl04JTt71Y/TTAxy7OX27aZf1Rpt0SjdZ7vTPnFDPNsHGe -KBKvPt55l2+YKjkZmV7eRevsVbpkNvNGB+T5d4Ge/wIBA6NPME0wDgYDVR0PAQH/ -BAQDAgCgMA0GA1UdDgQGBAQBAgMEMA8GA1UdIwQIMAaABAECAwQwGwYDVR0RBBQw -EoIJMTI3LjAuMC4xggVbOjoxXTALBgkqhkiG9w0BAQUDggCBAHC3gbdvc44vs+wD -g2kONiENnx8WKc0UTGg/TOXS3gaRb+CUIQtHWja65l8rAfclEovjHgZ7gx8brO0W -JuC6p3MUAKsgOssIrrRIx2rpnfcmFVMzguCmrMNVmKUAalw18Yp0F72xYAIitVQl -kJrLdIhBajcJRYu/YGltHQRaXuVt +MIIBOTCB5qADAgECAgEAMAsGCSqGSIb3DQEBBTAAMB4XDTcwMDEwMTAwMDAwMFoX +DTQ5MTIzMTIzNTk1OVowADBaMAsGCSqGSIb3DQEBAQNLADBIAkEAsuA5mAFMj6Q7 +qoBzcvKzIq4kzuT5epSp2AkcQfyBHm7K13Ws7u+0b5Vb9gqTf5cAiIKcrtrXVqkL +8i1UQF6AzwIDAQABo08wTTAOBgNVHQ8BAf8EBAMCACQwDQYDVR0OBAYEBAECAwQw +DwYDVR0jBAgwBoAEAQIDBDAbBgNVHREEFDASggkxMjcuMC4wLjGCBVs6OjFdMAsG +CSqGSIb3DQEBBQNBAJH30zjLWRztrWpOCgJL8RQWLaKzhK79pVhAx6q/3NrF16C7 ++l1BRZstTwIGdoGId8BRpErK1TXkniFb95ZMynM= -----END CERTIFICATE----- `) // localhostKey is the private key for localhostCert. var localhostKey = []byte(`-----BEGIN RSA PRIVATE KEY----- -MIIBkgIBAQKCAIB6oy4iT42G6qk+GGn5VL5JlnJT6ZG5cqaMNFaNGlIxNb6CPUZL -Kq2sM3gRaimsktIwnNAcNwQGHpe1tZo+J/Pl04JTt71Y/TTAxy7OX27aZf1Rpt0S -jdZ7vTPnFDPNsHGeKBKvPt55l2+YKjkZmV7eRevsVbpkNvNGB+T5d4Ge/wIBAwKC -AIBRwh7Bil5Z8cYpZZv7jdQxDvbim7Z7ocRdeDmzZuF2I9RW04QyHHPIIlALnBvI -YeF1veASz1gEFGUjzmbUGqKYSbCoTzXoev+F4bmbRxcX9sOmtslqvhMSHRSzA5NH -aDVI3Hn4wvBVD8gePu8ACWqvPGbCiql11OKCMfjlPn2uuwJAx/24/F5DjXZ6hQQ7 -HxScOxKrpx5WnA9r1wZTltOTZkhRRzuLc21WJeE3M15QUdWi3zZxCKRFoth65HEs -jy9YHQJAnPueRI44tz79b5QqVbeaOMUr7ZCb1Kp0uo6G+ANPLdlfliAupwij2eIz -mHRJOWk0jBtXfRft1McH2H51CpXAyw== +MIIBPQIBAAJBALLgOZgBTI+kO6qAc3LysyKuJM7k+XqUqdgJHEH8gR5uytd1rO7v +tG+VW/YKk3+XAIiCnK7a11apC/ItVEBegM8CAwEAAQJBAI5sxq7naeR9ahyqRkJi +SIv2iMxLuPEHaezf5CYOPWjSjBPyVhyRevkhtqEjF/WkgL7C2nWpYHsUcBDBQVF0 +3KECIQDtEGB2ulnkZAahl3WuJziXGLB+p8Wgx7wzSM6bHu1c6QIhAMEp++CaS+SJ +/TrU0zwY/fW4SvQeb49BPZUF3oqR8Xz3AiEA1rAJHBzBgdOQKdE3ksMUPcnvNJSN +poCcELmz2clVXtkCIQCLytuLV38XHToTipR4yMl6O+6arzAjZ56uq7m7ZRV0TwIh +AM65XAOw8Dsg9Kq78aYXiOEDc5DL0sbFUu/SlmRcCg93 -----END RSA PRIVATE KEY----- `) diff --git a/src/pkg/http/readrequest_test.go b/src/pkg/http/readrequest_test.go index 19e2ff774..d93e573f5 100644 --- a/src/pkg/http/readrequest_test.go +++ b/src/pkg/http/readrequest_test.go @@ -64,7 +64,7 @@ var reqTests = []reqTest{ Host: "www.techcrunch.com", Referer: "", UserAgent: "Fake", - Form: map[string][]string{}, + Form: Values{}, }, "abcdef\n", @@ -99,7 +99,7 @@ var reqTests = []reqTest{ Host: "test", Referer: "", UserAgent: "", - Form: map[string][]string{}, + Form: Values{}, }, "", diff --git a/src/pkg/http/request.go b/src/pkg/http/request.go index 2f6b651c3..bdc3a7e4f 100644 --- a/src/pkg/http/request.go +++ b/src/pkg/http/request.go @@ -90,10 +90,10 @@ type Request struct { // // then // - // Header = map[string]string{ - // "Accept-Encoding": "gzip, deflate", - // "Accept-Language": "en-us", - // "Connection": "keep-alive", + // Header = map[string][]string{ + // "Accept-Encoding": {"gzip, deflate"}, + // "Accept-Language": {"en-us"}, + // "Connection": {"keep-alive"}, // } // // HTTP defines that header names are case-insensitive. @@ -141,7 +141,7 @@ type Request struct { UserAgent string // The parsed form. Only available after ParseForm is called. - Form map[string][]string + Form Values // The parsed multipart form, including file uploads. // Only available after ParseMultipartForm is called. @@ -238,9 +238,9 @@ const defaultUserAgent = "Go http package" // TransferEncoding // Body // -// If Body is present but Content-Length is <= 0, Write adds -// "Transfer-Encoding: chunked" to the header. Body is closed after -// it is sent. +// If Body is present, Content-Length is <= 0 and TransferEncoding +// hasn't been set to "identity", Write adds "Transfer-Encoding: +// chunked" to the header. Body is closed after it is sent. func (req *Request) Write(w io.Writer) os.Error { return req.write(w, false) } @@ -488,6 +488,11 @@ func NewRequest(method, url string, body io.Reader) (*Request, os.Error) { default: req.ContentLength = -1 // chunked } + if req.ContentLength == 0 { + // To prevent chunking and disambiguate this + // from the default ContentLength zero value. + req.TransferEncoding = []string{"identity"} + } } return req, nil @@ -597,18 +602,56 @@ func ReadRequest(b *bufio.Reader) (req *Request, err os.Error) { return req, nil } +// Values maps a string key to a list of values. +// It is typically used for query parameters and form values. +// Unlike in the Header map, the keys in a Values map +// are case-sensitive. +type Values map[string][]string + +// Get gets the first value associated with the given key. +// If there are no values associated with the key, Get returns +// the empty string. To access multiple values, use the map +// directly. +func (v Values) Get(key string) string { + if v == nil { + return "" + } + vs, ok := v[key] + if !ok || len(vs) == 0 { + return "" + } + return vs[0] +} + +// Set sets the key to value. It replaces any existing +// values. +func (v Values) Set(key, value string) { + v[key] = []string{value} +} + +// Add adds the key to value. It appends to any existing +// values associated with key. +func (v Values) Add(key, value string) { + v[key] = append(v[key], value) +} + +// Del deletes the values associated with key. +func (v Values) Del(key string) { + v[key] = nil, false +} + // ParseQuery parses the URL-encoded query string and returns // a map listing the values specified for each key. // ParseQuery always returns a non-nil map containing all the // valid query parameters found; err describes the first decoding error // encountered, if any. -func ParseQuery(query string) (m map[string][]string, err os.Error) { - m = make(map[string][]string) +func ParseQuery(query string) (m Values, err os.Error) { + m = make(Values) err = parseQuery(m, query) return } -func parseQuery(m map[string][]string, query string) (err os.Error) { +func parseQuery(m Values, query string) (err os.Error) { for _, kv := range strings.Split(query, "&", -1) { if len(kv) == 0 { continue @@ -641,7 +684,7 @@ func (r *Request) ParseForm() (err os.Error) { return } - r.Form = make(map[string][]string) + r.Form = make(Values) if r.URL != nil { err = parseQuery(r.Form, r.URL.RawQuery) } diff --git a/src/pkg/http/requestwrite_test.go b/src/pkg/http/requestwrite_test.go index 2889048a9..98fbcf459 100644 --- a/src/pkg/http/requestwrite_test.go +++ b/src/pkg/http/requestwrite_test.go @@ -274,13 +274,45 @@ func (rc *closeChecker) Close() os.Error { // TestRequestWriteClosesBody tests that Request.Write does close its request.Body. // It also indirectly tests NewRequest and that it doesn't wrap an existing Closer -// inside a NopCloser. +// inside a NopCloser, and that it serializes it correctly. func TestRequestWriteClosesBody(t *testing.T) { rc := &closeChecker{Reader: strings.NewReader("my body")} - req, _ := NewRequest("GET", "http://foo.com/", rc) + req, _ := NewRequest("POST", "http://foo.com/", rc) + if g, e := req.ContentLength, int64(-1); g != e { + t.Errorf("got req.ContentLength %d, want %d", g, e) + } buf := new(bytes.Buffer) req.Write(buf) if !rc.closed { t.Error("body not closed after write") } + if g, e := buf.String(), "POST / HTTP/1.1\r\nHost: foo.com\r\nUser-Agent: Go http package\r\nTransfer-Encoding: chunked\r\n\r\n7\r\nmy body\r\n0\r\n\r\n"; g != e { + t.Errorf("write:\n got: %s\nwant: %s", g, e) + } +} + +func TestZeroLengthNewRequest(t *testing.T) { + var buf bytes.Buffer + + // Writing with default identity encoding + req, _ := NewRequest("PUT", "http://foo.com/", strings.NewReader("")) + if len(req.TransferEncoding) == 0 || req.TransferEncoding[0] != "identity" { + t.Fatalf("got req.TransferEncoding of %v, want %v", req.TransferEncoding, []string{"identity"}) + } + if g, e := req.ContentLength, int64(0); g != e { + t.Errorf("got req.ContentLength %d, want %d", g, e) + } + req.Write(&buf) + if g, e := buf.String(), "PUT / HTTP/1.1\r\nHost: foo.com\r\nUser-Agent: Go http package\r\nContent-Length: 0\r\n\r\n"; g != e { + t.Errorf("identity write:\n got: %s\nwant: %s", g, e) + } + + // Overriding identity encoding and forcing chunked. + req, _ = NewRequest("PUT", "http://foo.com/", strings.NewReader("")) + req.TransferEncoding = nil + buf.Reset() + req.Write(&buf) + if g, e := buf.String(), "PUT / HTTP/1.1\r\nHost: foo.com\r\nUser-Agent: Go http package\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n\r\n"; g != e { + t.Errorf("chunked write:\n got: %s\nwant: %s", g, e) + } } diff --git a/src/pkg/http/serve_test.go b/src/pkg/http/serve_test.go index c923c8a76..dc4594a79 100644 --- a/src/pkg/http/serve_test.go +++ b/src/pkg/http/serve_test.go @@ -12,12 +12,14 @@ import ( "fmt" . "http" "http/httptest" + "io" "io/ioutil" "log" "os" "net" "reflect" "strings" + "syscall" "testing" "time" ) @@ -494,6 +496,12 @@ func TestHeadResponses(t *testing.T) { if err != ErrBodyNotAllowed { t.Errorf("on Write, expected ErrBodyNotAllowed, got %v", err) } + + // Also exercise the ReaderFrom path + _, err = io.Copy(w, strings.NewReader("Ignored body")) + if err != ErrBodyNotAllowed { + t.Errorf("on Copy, expected ErrBodyNotAllowed, got %v", err) + } })) defer ts.Close() res, err := Head(ts.URL) @@ -773,6 +781,42 @@ func TestHandlerPanic(t *testing.T) { } } +type errorListener struct { + errs []os.Error +} + +func (l *errorListener) Accept() (c net.Conn, err os.Error) { + if len(l.errs) == 0 { + return nil, os.EOF + } + err = l.errs[0] + l.errs = l.errs[1:] + return +} + +func (l *errorListener) Close() os.Error { + return nil +} + +func (l *errorListener) Addr() net.Addr { + return dummyAddr("test-address") +} + +func TestAcceptMaxFds(t *testing.T) { + log.SetOutput(ioutil.Discard) // is noisy otherwise + defer log.SetOutput(os.Stderr) + + ln := &errorListener{[]os.Error{ + &net.OpError{ + Op: "accept", + Error: os.Errno(syscall.EMFILE), + }}} + err := Serve(ln, HandlerFunc(HandlerFunc(func(ResponseWriter, *Request) {}))) + if err != os.EOF { + t.Errorf("got error %v, want EOF", err) + } +} + func BenchmarkClientServer(b *testing.B) { b.StopTimer() ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, r *Request) { diff --git a/src/pkg/http/server.go b/src/pkg/http/server.go index 93d9d2ff4..d4638f127 100644 --- a/src/pkg/http/server.go +++ b/src/pkg/http/server.go @@ -129,7 +129,7 @@ func (r *response) ReadFrom(src io.Reader) (n int64, err os.Error) { // WriteHeader if it hasn't been called yet, and WriteHeader // is what sets r.chunking. r.Flush() - if !r.chunking { + if !r.chunking && r.bodyAllowed() { if rf, ok := r.conn.rwc.(io.ReaderFrom); ok { n, err = rf.ReadFrom(src) r.written += n @@ -335,6 +335,15 @@ func (w *response) WriteHeader(code int) { io.WriteString(w.conn.buf, "\r\n") } +// bodyAllowed returns true if a Write is allowed for this response type. +// It's illegal to call this before the header has been flushed. +func (w *response) bodyAllowed() bool { + if !w.wroteHeader { + panic("") + } + return w.status != StatusNotModified && w.req.Method != "HEAD" +} + func (w *response) Write(data []byte) (n int, err os.Error) { if w.conn.hijacked { log.Print("http: response.Write on hijacked connection") @@ -346,9 +355,7 @@ func (w *response) Write(data []byte) (n int, err os.Error) { if len(data) == 0 { return 0, nil } - - if w.status == StatusNotModified || w.req.Method == "HEAD" { - // Must not have body. + if !w.bodyAllowed() { return 0, ErrBodyNotAllowed } @@ -860,6 +867,10 @@ func (srv *Server) Serve(l net.Listener) os.Error { for { rw, e := l.Accept() if e != nil { + if ne, ok := e.(net.Error); ok && ne.Temporary() { + log.Printf("http: Accept error: %v", e) + continue + } return e } if srv.ReadTimeout != 0 { diff --git a/src/pkg/http/transfer.go b/src/pkg/http/transfer.go index 062e7a0ff..b54508e7a 100644 --- a/src/pkg/http/transfer.go +++ b/src/pkg/http/transfer.go @@ -38,6 +38,9 @@ func newTransferWriter(r interface{}) (t *transferWriter, err os.Error) { t.TransferEncoding = rr.TransferEncoding t.Trailer = rr.Trailer atLeastHTTP11 = rr.ProtoAtLeast(1, 1) + if t.Body != nil && t.ContentLength <= 0 && len(t.TransferEncoding) == 0 && atLeastHTTP11 { + t.TransferEncoding = []string{"chunked"} + } case *Response: t.Body = rr.Body t.ContentLength = rr.ContentLength @@ -95,7 +98,7 @@ func (t *transferWriter) WriteHeader(w io.Writer) (err os.Error) { if err != nil { return } - } else if t.ContentLength > 0 || t.ResponseToHEAD { + } else if t.ContentLength > 0 || t.ResponseToHEAD || (t.ContentLength == 0 && isIdentity(t.TransferEncoding)) { io.WriteString(w, "Content-Length: ") _, err = io.WriteString(w, strconv.Itoa64(t.ContentLength)+"\r\n") if err != nil { @@ -289,6 +292,9 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err os.Error) { // Checks whether chunked is part of the encodings stack func chunked(te []string) bool { return len(te) > 0 && te[0] == "chunked" } +// Checks whether the encoding is explicitly "identity". +func isIdentity(te []string) bool { return len(te) == 1 && te[0] == "identity" } + // Sanitize transfer encoding func fixTransferEncoding(requestMethod string, header Header) ([]string, os.Error) { raw, present := header["Transfer-Encoding"] diff --git a/src/pkg/http/url.go b/src/pkg/http/url.go index d7ee14ee8..05b1662d3 100644 --- a/src/pkg/http/url.go +++ b/src/pkg/http/url.go @@ -486,10 +486,14 @@ func (url *URL) String() string { return result } -// EncodeQuery encodes the query represented as a multimap. -func EncodeQuery(m map[string][]string) string { - parts := make([]string, 0, len(m)) // will be large enough for most uses - for k, vs := range m { +// Encode encodes the values into ``URL encoded'' form. +// e.g. "foo=bar&bar=baz" +func (v Values) Encode() string { + if v == nil { + return "" + } + parts := make([]string, 0, len(v)) // will be large enough for most uses + for k, vs := range v { prefix := URLEscape(k) + "=" for _, v := range vs { parts = append(parts, prefix+URLEscape(v)) @@ -593,3 +597,9 @@ func (base *URL) ResolveReference(ref *URL) *URL { url.Raw = url.String() return url } + +// Query parses RawQuery and returns the corresponding values. +func (u *URL) Query() Values { + v, _ := ParseQuery(u.RawQuery) + return v +} diff --git a/src/pkg/http/url_test.go b/src/pkg/http/url_test.go index d8863f3d3..eaec5872a 100644 --- a/src/pkg/http/url_test.go +++ b/src/pkg/http/url_test.go @@ -538,23 +538,21 @@ func TestUnescapeUserinfo(t *testing.T) { } } -type qMap map[string][]string - type EncodeQueryTest struct { - m qMap + m Values expected string expected1 string } var encodeQueryTests = []EncodeQueryTest{ {nil, "", ""}, - {qMap{"q": {"puppies"}, "oe": {"utf8"}}, "q=puppies&oe=utf8", "oe=utf8&q=puppies"}, - {qMap{"q": {"dogs", "&", "7"}}, "q=dogs&q=%26&q=7", "q=dogs&q=%26&q=7"}, + {Values{"q": {"puppies"}, "oe": {"utf8"}}, "q=puppies&oe=utf8", "oe=utf8&q=puppies"}, + {Values{"q": {"dogs", "&", "7"}}, "q=dogs&q=%26&q=7", "q=dogs&q=%26&q=7"}, } func TestEncodeQuery(t *testing.T) { for _, tt := range encodeQueryTests { - if q := EncodeQuery(tt.m); q != tt.expected && q != tt.expected1 { + if q := tt.m.Encode(); q != tt.expected && q != tt.expected1 { t.Errorf(`EncodeQuery(%+v) = %q, want %q`, tt.m, q, tt.expected) } } @@ -673,3 +671,28 @@ func TestResolveReference(t *testing.T) { } } + +func TestQueryValues(t *testing.T) { + u, _ := ParseURL("http://x.com?foo=bar&bar=1&bar=2") + v := u.Query() + if len(v) != 2 { + t.Errorf("got %d keys in Query values, want 2", len(v)) + } + if g, e := v.Get("foo"), "bar"; g != e { + t.Errorf("Get(foo) = %q, want %q", g, e) + } + // Case sensitive: + if g, e := v.Get("Foo"), ""; g != e { + t.Errorf("Get(Foo) = %q, want %q", g, e) + } + if g, e := v.Get("bar"), "1"; g != e { + t.Errorf("Get(bar) = %q, want %q", g, e) + } + if g, e := v.Get("baz"), ""; g != e { + t.Errorf("Get(baz) = %q, want %q", g, e) + } + v.Del("bar") + if g, e := v.Get("bar"), ""; g != e { + t.Errorf("second Get(bar) = %q, want %q", g, e) + } +} diff --git a/src/pkg/exp/draw/Makefile b/src/pkg/image/draw/Makefile index 6f0f0b2f5..2ba6e7b51 100644 --- a/src/pkg/exp/draw/Makefile +++ b/src/pkg/image/draw/Makefile @@ -4,9 +4,8 @@ include ../../../Make.inc -TARG=exp/draw +TARG=image/draw GOFILES=\ draw.go\ - event.go\ include ../../../Make.pkg diff --git a/src/pkg/image/draw/clip_test.go b/src/pkg/image/draw/clip_test.go new file mode 100644 index 000000000..db40d82f5 --- /dev/null +++ b/src/pkg/image/draw/clip_test.go @@ -0,0 +1,193 @@ +// Copyright 2011 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 draw + +import ( + "image" + "testing" +) + +type clipTest struct { + desc string + r, dr, sr, mr image.Rectangle + sp, mp image.Point + nilMask bool + r0 image.Rectangle + sp0, mp0 image.Point +} + +var clipTests = []clipTest{ + // The following tests all have a nil mask. + { + "basic", + image.Rect(0, 0, 100, 100), + image.Rect(0, 0, 100, 100), + image.Rect(0, 0, 100, 100), + image.ZR, + image.ZP, + image.ZP, + true, + image.Rect(0, 0, 100, 100), + image.ZP, + image.ZP, + }, + { + "clip dr", + image.Rect(0, 0, 100, 100), + image.Rect(40, 40, 60, 60), + image.Rect(0, 0, 100, 100), + image.ZR, + image.ZP, + image.ZP, + true, + image.Rect(40, 40, 60, 60), + image.Pt(40, 40), + image.ZP, + }, + { + "clip sr", + image.Rect(0, 0, 100, 100), + image.Rect(0, 0, 100, 100), + image.Rect(20, 20, 80, 80), + image.ZR, + image.ZP, + image.ZP, + true, + image.Rect(20, 20, 80, 80), + image.Pt(20, 20), + image.ZP, + }, + { + "clip dr and sr", + image.Rect(0, 0, 100, 100), + image.Rect(0, 0, 50, 100), + image.Rect(20, 20, 80, 80), + image.ZR, + image.ZP, + image.ZP, + true, + image.Rect(20, 20, 50, 80), + image.Pt(20, 20), + image.ZP, + }, + { + "clip dr and sr, sp outside sr (top-left)", + image.Rect(0, 0, 100, 100), + image.Rect(0, 0, 50, 100), + image.Rect(20, 20, 80, 80), + image.ZR, + image.Pt(15, 8), + image.ZP, + true, + image.Rect(5, 12, 50, 72), + image.Pt(20, 20), + image.ZP, + }, + { + "clip dr and sr, sp outside sr (middle-left)", + image.Rect(0, 0, 100, 100), + image.Rect(0, 0, 50, 100), + image.Rect(20, 20, 80, 80), + image.ZR, + image.Pt(15, 66), + image.ZP, + true, + image.Rect(5, 0, 50, 14), + image.Pt(20, 66), + image.ZP, + }, + { + "clip dr and sr, sp outside sr (bottom-left)", + image.Rect(0, 0, 100, 100), + image.Rect(0, 0, 50, 100), + image.Rect(20, 20, 80, 80), + image.ZR, + image.Pt(15, 91), + image.ZP, + true, + image.ZR, + image.Pt(15, 91), + image.ZP, + }, + { + "clip dr and sr, sp inside sr", + image.Rect(0, 0, 100, 100), + image.Rect(0, 0, 50, 100), + image.Rect(20, 20, 80, 80), + image.ZR, + image.Pt(44, 33), + image.ZP, + true, + image.Rect(0, 0, 36, 47), + image.Pt(44, 33), + image.ZP, + }, + + // The following tests all have a non-nil mask. + { + "basic mask", + image.Rect(0, 0, 80, 80), + image.Rect(20, 0, 100, 80), + image.Rect(0, 0, 50, 49), + image.Rect(0, 0, 46, 47), + image.ZP, + image.ZP, + false, + image.Rect(20, 0, 46, 47), + image.Pt(20, 0), + image.Pt(20, 0), + }, + // TODO(nigeltao): write more tests. +} + +func TestClip(t *testing.T) { + dst0 := image.NewRGBA(100, 100) + src0 := image.NewRGBA(100, 100) + mask0 := image.NewRGBA(100, 100) + for _, c := range clipTests { + dst := dst0.SubImage(c.dr).(*image.RGBA) + src := src0.SubImage(c.sr).(*image.RGBA) + var mask image.Image + if !c.nilMask { + mask = mask0.SubImage(c.mr) + } + r, sp, mp := c.r, c.sp, c.mp + clip(dst, &r, src, &sp, mask, &mp) + + // Check that the actual results equal the expected results. + if !c.r0.Eq(r) { + t.Errorf("%s: clip rectangle want %v got %v", c.desc, c.r0, r) + continue + } + if !c.sp0.Eq(sp) { + t.Errorf("%s: sp want %v got %v", c.desc, c.sp0, sp) + continue + } + if !c.nilMask { + if !c.mp0.Eq(mp) { + t.Errorf("%s: mp want %v got %v", c.desc, c.mp0, mp) + continue + } + } + + // Check that the clipped rectangle is contained by the dst / src / mask + // rectangles, in their respective co-ordinate spaces. + if !r.In(c.dr) { + t.Errorf("%s: c.dr %v does not contain r %v", c.desc, c.dr, r) + } + // sr is r translated into src's co-ordinate space. + sr := r.Add(c.sp.Sub(c.dr.Min)) + if !sr.In(c.sr) { + t.Errorf("%s: c.sr %v does not contain sr %v", c.desc, c.sr, sr) + } + if !c.nilMask { + // mr is r translated into mask's co-ordinate space. + mr := r.Add(c.mp.Sub(c.dr.Min)) + if !mr.In(c.mr) { + t.Errorf("%s: c.mr %v does not contain mr %v", c.desc, c.mr, mr) + } + } + } +} diff --git a/src/pkg/exp/draw/draw.go b/src/pkg/image/draw/draw.go index f98e24618..618fb4aa6 100644 --- a/src/pkg/exp/draw/draw.go +++ b/src/pkg/image/draw/draw.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package draw provides basic graphics and drawing primitives, +// Package draw provides image composition functions // in the style of the Plan 9 graphics library // (see http://plan9.bell-labs.com/magic/man2html/2/draw) // and the X Render extension. @@ -16,7 +16,7 @@ import ( // m is the maximum color value returned by image.Color.RGBA. const m = 1<<16 - 1 -// A Porter-Duff compositing operator. +// Op is a Porter-Duff compositing operator. type Op int const ( @@ -39,27 +39,31 @@ func Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point) { DrawMask(dst, r, src, sp, nil, image.ZP, Over) } -// DrawMask aligns r.Min in dst with sp in src and mp in mask and then replaces the rectangle r -// in dst with the result of a Porter-Duff composition. A nil mask is treated as opaque. -func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) { - sb := src.Bounds() - dx, dy := sb.Max.X-sp.X, sb.Max.Y-sp.Y +// clip clips r against each image's bounds (after translating into the +// destination image's co-ordinate space) and shifts the points sp and mp by +// the same amount as the change in r.Min. +func clip(dst Image, r *image.Rectangle, src image.Image, sp *image.Point, mask image.Image, mp *image.Point) { + orig := r.Min + *r = r.Intersect(dst.Bounds()) + *r = r.Intersect(src.Bounds().Add(orig.Sub(*sp))) if mask != nil { - mb := mask.Bounds() - if dx > mb.Max.X-mp.X { - dx = mb.Max.X - mp.X - } - if dy > mb.Max.Y-mp.Y { - dy = mb.Max.Y - mp.Y - } - } - if r.Dx() > dx { - r.Max.X = r.Min.X + dx + *r = r.Intersect(mask.Bounds().Add(orig.Sub(*mp))) } - if r.Dy() > dy { - r.Max.Y = r.Min.Y + dy + dx := r.Min.X - orig.X + dy := r.Min.Y - orig.Y + if dx == 0 && dy == 0 { + return } - r = r.Intersect(dst.Bounds()) + (*sp).X += dx + (*sp).Y += dy + (*mp).X += dx + (*mp).Y += dy +} + +// DrawMask aligns r.Min in dst with sp in src and mp in mask and then replaces the rectangle r +// in dst with the result of a Porter-Duff composition. A nil mask is treated as opaque. +func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) { + clip(dst, &r, src, &sp, mask, &mp) if r.Empty() { return } diff --git a/src/pkg/exp/draw/draw_test.go b/src/pkg/image/draw/draw_test.go index 873a2f24a..37d630353 100644 --- a/src/pkg/exp/draw/draw_test.go +++ b/src/pkg/image/draw/draw_test.go @@ -263,8 +263,8 @@ func TestDrawOverlap(t *testing.T) { } } -// TestIssue836 verifies http://code.google.com/p/go/issues/detail?id=836. -func TestIssue836(t *testing.T) { +// TestNonZeroSrcPt checks drawing with a non-zero src point parameter. +func TestNonZeroSrcPt(t *testing.T) { a := image.NewRGBA(1, 1) b := image.NewRGBA(2, 2) b.Set(0, 0, image.RGBAColor{0, 0, 0, 5}) @@ -273,6 +273,6 @@ func TestIssue836(t *testing.T) { b.Set(1, 1, image.RGBAColor{5, 0, 0, 5}) Draw(a, image.Rect(0, 0, 1, 1), b, image.Pt(1, 1)) if !eq(image.RGBAColor{5, 0, 0, 5}, a.At(0, 0)) { - t.Errorf("Issue 836: want %v got %v", image.RGBAColor{5, 0, 0, 5}, a.At(0, 0)) + t.Errorf("non-zero src pt: want %v got %v", image.RGBAColor{5, 0, 0, 5}, a.At(0, 0)) } } diff --git a/src/pkg/image/geom.go b/src/pkg/image/geom.go index ccfe9cdb0..667aee625 100644 --- a/src/pkg/image/geom.go +++ b/src/pkg/image/geom.go @@ -38,6 +38,12 @@ func (p Point) Div(k int) Point { return Point{p.X / k, p.Y / k} } +// In returns whether p is in r. +func (p Point) In(r Rectangle) bool { + return r.Min.X <= p.X && p.X < r.Max.X && + r.Min.Y <= p.Y && p.Y < r.Max.Y +} + // Mod returns the point q in r such that p.X-q.X is a multiple of r's width // and p.Y-q.Y is a multiple of r's height. func (p Point) Mod(r Rectangle) Point { @@ -190,10 +196,15 @@ func (r Rectangle) Overlaps(s Rectangle) bool { r.Min.Y < s.Max.Y && s.Min.Y < r.Max.Y } -// Contains returns whether r contains p. -func (r Rectangle) Contains(p Point) bool { - return p.X >= r.Min.X && p.X < r.Max.X && - p.Y >= r.Min.Y && p.Y < r.Max.Y +// In returns whether every point in r is in s. +func (r Rectangle) In(s Rectangle) bool { + if r.Empty() { + return true + } + // Note that r.Max is an exclusive bound for r, so that r.In(s) + // does not require that r.Max.In(s). + return s.Min.X <= r.Min.X && r.Max.X <= s.Max.X && + s.Min.Y <= r.Min.Y && r.Max.Y <= s.Max.Y } // Canon returns the canonical version of r. The returned rectangle has minimum diff --git a/src/pkg/image/gif/reader.go b/src/pkg/image/gif/reader.go index 9f7296a98..26c013b9a 100644 --- a/src/pkg/image/gif/reader.go +++ b/src/pkg/image/gif/reader.go @@ -274,7 +274,7 @@ func (d *decoder) readExtension() os.Error { return fmt.Errorf("gif: unknown extension 0x%.2x", extension) } if size > 0 { - if _, err := d.r.Read(d.tmp[0:size]); err != nil { + if _, err := io.ReadFull(d.r, d.tmp[0:size]); err != nil { return err } } @@ -406,8 +406,8 @@ func DecodeAll(r io.Reader) (*GIF, os.Error) { return gif, nil } -// DecodeConfig returns the color model and dimensions of a GIF image without -// decoding the entire image. +// DecodeConfig returns the global color model and dimensions of a GIF image +// without decoding the entire image. func DecodeConfig(r io.Reader) (image.Config, os.Error) { var d decoder if err := d.decode(r, true); err != nil { diff --git a/src/pkg/image/image.go b/src/pkg/image/image.go index 1bdac36f5..f4c38d28a 100644 --- a/src/pkg/image/image.go +++ b/src/pkg/image/image.go @@ -38,21 +38,21 @@ func (p *RGBA) ColorModel() ColorModel { return RGBAColorModel } func (p *RGBA) Bounds() Rectangle { return p.Rect } func (p *RGBA) At(x, y int) Color { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return RGBAColor{} } return p.Pix[y*p.Stride+x] } func (p *RGBA) Set(x, y int, c Color) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = toRGBAColor(c).(RGBAColor) } func (p *RGBA) SetRGBA(x, y int, c RGBAColor) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = c @@ -107,21 +107,21 @@ func (p *RGBA64) ColorModel() ColorModel { return RGBA64ColorModel } func (p *RGBA64) Bounds() Rectangle { return p.Rect } func (p *RGBA64) At(x, y int) Color { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return RGBA64Color{} } return p.Pix[y*p.Stride+x] } func (p *RGBA64) Set(x, y int, c Color) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = toRGBA64Color(c).(RGBA64Color) } func (p *RGBA64) SetRGBA64(x, y int, c RGBA64Color) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = c @@ -176,21 +176,21 @@ func (p *NRGBA) ColorModel() ColorModel { return NRGBAColorModel } func (p *NRGBA) Bounds() Rectangle { return p.Rect } func (p *NRGBA) At(x, y int) Color { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return NRGBAColor{} } return p.Pix[y*p.Stride+x] } func (p *NRGBA) Set(x, y int, c Color) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = toNRGBAColor(c).(NRGBAColor) } func (p *NRGBA) SetNRGBA(x, y int, c NRGBAColor) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = c @@ -245,21 +245,21 @@ func (p *NRGBA64) ColorModel() ColorModel { return NRGBA64ColorModel } func (p *NRGBA64) Bounds() Rectangle { return p.Rect } func (p *NRGBA64) At(x, y int) Color { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return NRGBA64Color{} } return p.Pix[y*p.Stride+x] } func (p *NRGBA64) Set(x, y int, c Color) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = toNRGBA64Color(c).(NRGBA64Color) } func (p *NRGBA64) SetNRGBA64(x, y int, c NRGBA64Color) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = c @@ -314,21 +314,21 @@ func (p *Alpha) ColorModel() ColorModel { return AlphaColorModel } func (p *Alpha) Bounds() Rectangle { return p.Rect } func (p *Alpha) At(x, y int) Color { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return AlphaColor{} } return p.Pix[y*p.Stride+x] } func (p *Alpha) Set(x, y int, c Color) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = toAlphaColor(c).(AlphaColor) } func (p *Alpha) SetAlpha(x, y int, c AlphaColor) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = c @@ -383,21 +383,21 @@ func (p *Alpha16) ColorModel() ColorModel { return Alpha16ColorModel } func (p *Alpha16) Bounds() Rectangle { return p.Rect } func (p *Alpha16) At(x, y int) Color { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return Alpha16Color{} } return p.Pix[y*p.Stride+x] } func (p *Alpha16) Set(x, y int, c Color) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = toAlpha16Color(c).(Alpha16Color) } func (p *Alpha16) SetAlpha16(x, y int, c Alpha16Color) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = c @@ -452,21 +452,21 @@ func (p *Gray) ColorModel() ColorModel { return GrayColorModel } func (p *Gray) Bounds() Rectangle { return p.Rect } func (p *Gray) At(x, y int) Color { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return GrayColor{} } return p.Pix[y*p.Stride+x] } func (p *Gray) Set(x, y int, c Color) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = toGrayColor(c).(GrayColor) } func (p *Gray) SetGray(x, y int, c GrayColor) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = c @@ -507,21 +507,21 @@ func (p *Gray16) ColorModel() ColorModel { return Gray16ColorModel } func (p *Gray16) Bounds() Rectangle { return p.Rect } func (p *Gray16) At(x, y int) Color { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return Gray16Color{} } return p.Pix[y*p.Stride+x] } func (p *Gray16) Set(x, y int, c Color) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = toGray16Color(c).(Gray16Color) } func (p *Gray16) SetGray16(x, y int, c Gray16Color) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = c @@ -563,14 +563,19 @@ func (p PalettedColorModel) Convert(c Color) Color { if len(p) == 0 { return nil } + return p[p.Index(c)] +} + +// Index returns the index of the palette color closest to c in Euclidean +// R,G,B space. +func (p PalettedColorModel) Index(c Color) int { cr, cg, cb, _ := c.RGBA() // Shift by 1 bit to avoid potential uint32 overflow in sum-squared-difference. cr >>= 1 cg >>= 1 cb >>= 1 - result := Color(nil) - bestSSD := uint32(1<<32 - 1) - for _, v := range p { + ret, bestSSD := 0, uint32(1<<32-1) + for i, v := range p { vr, vg, vb, _ := v.RGBA() vr >>= 1 vg >>= 1 @@ -578,11 +583,10 @@ func (p PalettedColorModel) Convert(c Color) Color { dr, dg, db := diff(cr, vr), diff(cg, vg), diff(cb, vb) ssd := (dr * dr) + (dg * dg) + (db * db) if ssd < bestSSD { - bestSSD = ssd - result = v + ret, bestSSD = i, ssd } } - return result + return ret } // A Paletted is an in-memory image backed by a 2-D slice of uint8 values and a PalettedColorModel. @@ -604,21 +608,28 @@ func (p *Paletted) At(x, y int) Color { if len(p.Palette) == 0 { return nil } - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return p.Palette[0] } return p.Palette[p.Pix[y*p.Stride+x]] } +func (p *Paletted) Set(x, y int, c Color) { + if !(Point{x, y}.In(p.Rect)) { + return + } + p.Pix[y*p.Stride+x] = uint8(p.Palette.Index(c)) +} + func (p *Paletted) ColorIndexAt(x, y int) uint8 { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return 0 } return p.Pix[y*p.Stride+x] } func (p *Paletted) SetColorIndex(x, y int, index uint8) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = index diff --git a/src/pkg/image/image_test.go b/src/pkg/image/image_test.go new file mode 100644 index 000000000..17e314795 --- /dev/null +++ b/src/pkg/image/image_test.go @@ -0,0 +1,71 @@ +// Copyright 2011 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 image + +import ( + "testing" +) + +func cmp(t *testing.T, cm ColorModel, c0, c1 Color) bool { + r0, g0, b0, a0 := cm.Convert(c0).RGBA() + r1, g1, b1, a1 := cm.Convert(c1).RGBA() + return r0 == r1 && g0 == g1 && b0 == b1 && a0 == a1 +} + +func TestImage(t *testing.T) { + type buffered interface { + Image + Set(int, int, Color) + SubImage(Rectangle) Image + } + testImage := []Image{ + NewRGBA(10, 10), + NewRGBA64(10, 10), + NewNRGBA(10, 10), + NewNRGBA64(10, 10), + NewAlpha(10, 10), + NewAlpha16(10, 10), + NewGray(10, 10), + NewGray16(10, 10), + NewPaletted(10, 10, PalettedColorModel{ + Transparent, + Opaque, + }), + } + for _, m := range testImage { + b := m.(buffered) + if !Rect(0, 0, 10, 10).Eq(b.Bounds()) { + t.Errorf("%T: want bounds %v, got %v", b, Rect(0, 0, 10, 10), b.Bounds()) + continue + } + if !cmp(t, b.ColorModel(), Transparent, b.At(6, 3)) { + t.Errorf("%T: at (6, 3), want a zero color, got %v", b, b.At(6, 3)) + continue + } + b.Set(6, 3, Opaque) + if !cmp(t, b.ColorModel(), Opaque, b.At(6, 3)) { + t.Errorf("%T: at (6, 3), want a non-zero color, got %v", b, b.At(6, 3)) + continue + } + b = b.SubImage(Rect(3, 2, 9, 8)).(buffered) + if !Rect(3, 2, 9, 8).Eq(b.Bounds()) { + t.Errorf("%T: sub-image want bounds %v, got %v", b, Rect(3, 2, 9, 8), b.Bounds()) + continue + } + if !cmp(t, b.ColorModel(), Opaque, b.At(6, 3)) { + t.Errorf("%T: sub-image at (6, 3), want a non-zero color, got %v", b, b.At(6, 3)) + continue + } + if !cmp(t, b.ColorModel(), Transparent, b.At(3, 3)) { + t.Errorf("%T: sub-image at (3, 3), want a zero color, got %v", b, b.At(3, 3)) + continue + } + b.Set(3, 3, Opaque) + if !cmp(t, b.ColorModel(), Opaque, b.At(3, 3)) { + t.Errorf("%T: sub-image at (3, 3), want a non-zero color, got %v", b, b.At(3, 3)) + continue + } + } +} diff --git a/src/pkg/image/tiff/reader.go b/src/pkg/image/tiff/reader.go index 57a7be4a2..26e52144d 100644 --- a/src/pkg/image/tiff/reader.go +++ b/src/pkg/image/tiff/reader.go @@ -46,6 +46,11 @@ type decoder struct { mode imageMode features map[int][]uint palette []image.Color + + buf []byte + off int // Current offset in buf. + v uint32 // Buffer value for reading with arbitrary bit depths. + nbits uint // Remaining number of bits in v. } // firstVal returns the first uint of the features entry with the given tag, @@ -151,78 +156,94 @@ func (d *decoder) parseIFD(p []byte) os.Error { return nil } -// decode decodes the raw data of an image with 8 bits in each sample. -// It reads from p and writes the strip with ymin <= y < ymax into dst. -func (d *decoder) decode(dst image.Image, p []byte, ymin, ymax int) os.Error { +// readBits reads n bits from the internal buffer starting at the current offset. +func (d *decoder) readBits(n uint) uint32 { + for d.nbits < n { + d.v <<= 8 + d.v |= uint32(d.buf[d.off]) + d.off++ + d.nbits += 8 + } + d.nbits -= n + rv := d.v >> d.nbits + d.v &^= rv << d.nbits + return rv +} + +// flushBits discards the unread bits in the buffer used by readBits. +// It is used at the end of a line. +func (d *decoder) flushBits() { + d.v = 0 + d.nbits = 0 +} + +// decode decodes the raw data of an image. +// It reads from d.buf and writes the strip with ymin <= y < ymax into dst. +func (d *decoder) decode(dst image.Image, ymin, ymax int) os.Error { spp := len(d.features[tBitsPerSample]) // samples per pixel - off := 0 + d.off = 0 width := dst.Bounds().Dx() - if len(p) < spp*(ymax-ymin)*width { - return FormatError("short data strip") - } - // Apply horizontal predictor if necessary. // In this case, p contains the color difference to the preceding pixel. // See page 64-65 of the spec. - if d.firstVal(tPredictor) == prHorizontal { + if d.firstVal(tPredictor) == prHorizontal && d.firstVal(tBitsPerSample) == 8 { for y := ymin; y < ymax; y++ { - off += spp + d.off += spp for x := 0; x < (width-1)*spp; x++ { - p[off] += p[off-spp] - off++ + d.buf[d.off] += d.buf[d.off-spp] + d.off++ } } - off = 0 + d.off = 0 } switch d.mode { - case mGray: - img := dst.(*image.Gray) - for y := ymin; y < ymax; y++ { - for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ { - img.Set(x, y, image.GrayColor{p[off]}) - off += spp - } - } - case mGrayInvert: + case mGray, mGrayInvert: img := dst.(*image.Gray) + bpp := d.firstVal(tBitsPerSample) + max := uint32((1 << bpp) - 1) for y := ymin; y < ymax; y++ { for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ { - img.Set(x, y, image.GrayColor{0xff - p[off]}) - off += spp + v := uint8(d.readBits(bpp) * 0xff / max) + if d.mode == mGrayInvert { + v = 0xff - v + } + img.SetGray(x, y, image.GrayColor{v}) } + d.flushBits() } case mPaletted: img := dst.(*image.Paletted) + bpp := d.firstVal(tBitsPerSample) for y := ymin; y < ymax; y++ { for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ { - img.SetColorIndex(x, y, p[off]) - off += spp + img.SetColorIndex(x, y, uint8(d.readBits(bpp))) } + d.flushBits() } case mRGB: img := dst.(*image.RGBA) for y := ymin; y < ymax; y++ { for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ { - img.Set(x, y, image.RGBAColor{p[off], p[off+1], p[off+2], 0xff}) - off += spp + img.SetRGBA(x, y, image.RGBAColor{d.buf[d.off], d.buf[d.off+1], d.buf[d.off+2], 0xff}) + d.off += spp } } case mNRGBA: img := dst.(*image.NRGBA) for y := ymin; y < ymax; y++ { for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ { - img.Set(x, y, image.NRGBAColor{p[off], p[off+1], p[off+2], p[off+3]}) - off += spp + img.SetNRGBA(x, y, image.NRGBAColor{d.buf[d.off], d.buf[d.off+1], d.buf[d.off+2], d.buf[d.off+3]}) + d.off += spp } } case mRGBA: img := dst.(*image.RGBA) for y := ymin; y < ymax; y++ { for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ { - img.Set(x, y, image.RGBAColor{p[off], p[off+1], p[off+2], p[off+3]}) - off += spp + img.SetRGBA(x, y, image.RGBAColor{d.buf[d.off], d.buf[d.off+1], d.buf[d.off+2], d.buf[d.off+3]}) + d.off += spp } } } @@ -272,9 +293,18 @@ func newDecoder(r io.Reader) (*decoder, os.Error) { d.config.Width = int(d.firstVal(tImageWidth)) d.config.Height = int(d.firstVal(tImageLength)) + if _, ok := d.features[tBitsPerSample]; !ok { + return nil, FormatError("BitsPerSample tag missing") + } + // Determine the image mode. switch d.firstVal(tPhotometricInterpretation) { case pRGB: + for _, b := range d.features[tBitsPerSample] { + if b != 8 { + return nil, UnsupportedError("non-8-bit RGB image") + } + } d.config.ColorModel = image.RGBAColorModel // RGB images normally have 3 samples per pixel. // If there are more, ExtraSamples (p. 31-32 of the spec) @@ -309,15 +339,6 @@ func newDecoder(r io.Reader) (*decoder, os.Error) { return nil, UnsupportedError("color model") } - if _, ok := d.features[tBitsPerSample]; !ok { - return nil, FormatError("BitsPerSample tag missing") - } - for _, b := range d.features[tBitsPerSample] { - if b != 8 { - return nil, UnsupportedError("not an 8-bit image") - } - } - return d, nil } @@ -357,7 +378,6 @@ func Decode(r io.Reader) (img image.Image, err os.Error) { img = image.NewRGBA(d.config.Width, d.config.Height) } - var p []byte for i := 0; i < numStrips; i++ { ymin := i * rps // The last strip may be shorter. @@ -369,18 +389,18 @@ func Decode(r io.Reader) (img image.Image, err os.Error) { switch d.firstVal(tCompression) { case cNone: // TODO(bsiegert): Avoid copy if r is a tiff.buffer. - p = make([]byte, 0, n) - _, err = d.r.ReadAt(p, offset) + d.buf = make([]byte, n) + _, err = d.r.ReadAt(d.buf, offset) case cLZW: r := lzw.NewReader(io.NewSectionReader(d.r, offset, n), lzw.MSB, 8) - p, err = ioutil.ReadAll(r) + d.buf, err = ioutil.ReadAll(r) r.Close() case cDeflate, cDeflateOld: r, err := zlib.NewReader(io.NewSectionReader(d.r, offset, n)) if err != nil { return nil, err } - p, err = ioutil.ReadAll(r) + d.buf, err = ioutil.ReadAll(r) r.Close() default: err = UnsupportedError("compression") @@ -388,7 +408,7 @@ func Decode(r io.Reader) (img image.Image, err os.Error) { if err != nil { return } - err = d.decode(img, p, ymin, ymin+rps) + err = d.decode(img, ymin, ymin+rps) } return } diff --git a/src/pkg/image/ycbcr/ycbcr.go b/src/pkg/image/ycbcr/ycbcr.go index c1c58b708..f2de3d6fb 100644 --- a/src/pkg/image/ycbcr/ycbcr.go +++ b/src/pkg/image/ycbcr/ycbcr.go @@ -142,7 +142,7 @@ func (p *YCbCr) Bounds() image.Rectangle { } func (p *YCbCr) At(x, y int) image.Color { - if !p.Rect.Contains(image.Point{x, y}) { + if !(image.Point{x, y}.In(p.Rect)) { return YCbCrColor{} } switch p.SubsampleRatio { diff --git a/src/pkg/mail/message.go b/src/pkg/mail/message.go index 9723863fe..754b779be 100644 --- a/src/pkg/mail/message.go +++ b/src/pkg/mail/message.go @@ -2,17 +2,42 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package mail implements parsing of mail messages according to RFC 5322. +/* +Package mail implements parsing of mail messages. + +For the most part, this package follows the syntax as specified by RFC 5322. +Notable divergences: + * Obsolete address formats are not parsed, including addresses with + embedded route information. + * Group addresses are not parsed. + * The full range of spacing (the CFWS syntax element) is not supported, + such as breaking addresses across lines. +*/ package mail import ( "bufio" + "bytes" + "fmt" "io" + "log" "net/textproto" "os" + "strconv" + "strings" "time" ) +var debug = debugT(false) + +type debugT bool + +func (d debugT) Printf(format string, args ...interface{}) { + if d { + log.Printf(format, args...) + } +} + // A Message represents a parsed mail message. type Message struct { Header Header @@ -72,8 +97,6 @@ func parseDate(date string) (*time.Time, os.Error) { return nil, os.ErrorString("mail: header could not be parsed") } -// TODO(dsymonds): Parsers for more specific headers such as To, From, etc. - // A Header represents the key-value pairs in a mail message header. type Header map[string][]string @@ -93,3 +116,376 @@ func (h Header) Date() (*time.Time, os.Error) { } return parseDate(hdr) } + +// AddressList parses the named header field as a list of addresses. +func (h Header) AddressList(key string) ([]*Address, os.Error) { + hdr := h.Get(key) + if hdr == "" { + return nil, ErrHeaderNotPresent + } + return newAddrParser(hdr).parseAddressList() +} + +// Address represents a single mail address. +// An address such as "Barry Gibbs <bg@example.com>" is represented +// as Address{Name: "Barry Gibbs", Address: "bg@example.com"}. +type Address struct { + Name string // Proper name; may be empty. + Address string // user@domain +} + +// String formats the address as a valid RFC 5322 address. +// If the address's name contains non-ASCII characters +// the name will be rendered according to RFC 2047. +func (a *Address) String() string { + s := "<" + a.Address + ">" + if a.Name == "" { + return s + } + // If every character is printable ASCII, quoting is simple. + allPrintable := true + for i := 0; i < len(a.Name); i++ { + if !isVchar(a.Name[i]) { + allPrintable = false + break + } + } + if allPrintable { + b := bytes.NewBufferString(`"`) + for i := 0; i < len(a.Name); i++ { + if !isQtext(a.Name[i]) { + b.WriteByte('\\') + } + b.WriteByte(a.Name[i]) + } + b.WriteString(`" `) + b.WriteString(s) + return b.String() + } + + // UTF-8 "Q" encoding + b := bytes.NewBufferString("=?utf-8?q?") + for i := 0; i < len(a.Name); i++ { + switch c := a.Name[i]; { + case c == ' ': + b.WriteByte('_') + case isVchar(c) && c != '=' && c != '?' && c != '_': + b.WriteByte(c) + default: + fmt.Fprintf(b, "=%02X", c) + } + } + b.WriteString("?= ") + b.WriteString(s) + return b.String() +} + +type addrParser []byte + +func newAddrParser(s string) *addrParser { + p := addrParser([]byte(s)) + return &p +} + +func (p *addrParser) parseAddressList() ([]*Address, os.Error) { + var list []*Address + for { + p.skipSpace() + addr, err := p.parseAddress() + if err != nil { + return nil, err + } + list = append(list, addr) + + p.skipSpace() + if p.empty() { + break + } + if !p.consume(',') { + return nil, os.ErrorString("mail: expected comma") + } + } + return list, nil +} + +// parseAddress parses a single RFC 5322 address at the start of p. +func (p *addrParser) parseAddress() (addr *Address, err os.Error) { + debug.Printf("parseAddress: %q", *p) + p.skipSpace() + if p.empty() { + return nil, os.ErrorString("mail: no address") + } + + // address = name-addr / addr-spec + // TODO(dsymonds): Support parsing group address. + + // addr-spec has a more restricted grammar than name-addr, + // so try parsing it first, and fallback to name-addr. + // TODO(dsymonds): Is this really correct? + spec, err := p.consumeAddrSpec() + if err == nil { + return &Address{ + Address: spec, + }, err + } + debug.Printf("parseAddress: not an addr-spec: %v", err) + debug.Printf("parseAddress: state is now %q", *p) + + // display-name + var displayName string + if p.peek() != '<' { + displayName, err = p.consumePhrase() + if err != nil { + return nil, err + } + } + debug.Printf("parseAddress: displayName=%q", displayName) + + // angle-addr = "<" addr-spec ">" + p.skipSpace() + if !p.consume('<') { + return nil, os.ErrorString("mail: no angle-addr") + } + spec, err = p.consumeAddrSpec() + if err != nil { + return nil, err + } + if !p.consume('>') { + return nil, os.ErrorString("mail: unclosed angle-addr") + } + debug.Printf("parseAddress: spec=%q", spec) + + return &Address{ + Name: displayName, + Address: spec, + }, nil +} + +// consumeAddrSpec parses a single RFC 5322 addr-spec at the start of p. +func (p *addrParser) consumeAddrSpec() (spec string, err os.Error) { + debug.Printf("consumeAddrSpec: %q", *p) + + orig := *p + defer func() { + if err != nil { + *p = orig + } + }() + + // local-part = dot-atom / quoted-string + var localPart string + p.skipSpace() + if p.empty() { + return "", os.ErrorString("mail: no addr-spec") + } + if p.peek() == '"' { + // quoted-string + debug.Printf("consumeAddrSpec: parsing quoted-string") + localPart, err = p.consumeQuotedString() + } else { + // dot-atom + debug.Printf("consumeAddrSpec: parsing dot-atom") + localPart, err = p.consumeAtom(true) + } + if err != nil { + debug.Printf("consumeAddrSpec: failed: %v", err) + return "", err + } + + if !p.consume('@') { + return "", os.ErrorString("mail: missing @ in addr-spec") + } + + // domain = dot-atom / domain-literal + var domain string + p.skipSpace() + if p.empty() { + return "", os.ErrorString("mail: no domain in addr-spec") + } + // TODO(dsymonds): Handle domain-literal + domain, err = p.consumeAtom(true) + if err != nil { + return "", err + } + + return localPart + "@" + domain, nil +} + +// consumePhrase parses the RFC 5322 phrase at the start of p. +func (p *addrParser) consumePhrase() (phrase string, err os.Error) { + debug.Printf("consumePhrase: [%s]", *p) + // phrase = 1*word + var words []string + for { + // word = atom / quoted-string + var word string + p.skipSpace() + if p.empty() { + return "", os.ErrorString("mail: missing phrase") + } + if p.peek() == '"' { + // quoted-string + word, err = p.consumeQuotedString() + } else { + // atom + word, err = p.consumeAtom(false) + } + + // RFC 2047 encoded-word starts with =?, ends with ?=, and has two other ?s. + if err == nil && strings.HasPrefix(word, "=?") && strings.HasSuffix(word, "?=") && strings.Count(word, "?") == 4 { + word, err = decodeRFC2047Word(word) + } + + if err != nil { + break + } + debug.Printf("consumePhrase: consumed %q", word) + words = append(words, word) + } + // Ignore any error if we got at least one word. + if err != nil && len(words) == 0 { + debug.Printf("consumePhrase: hit err: %v", err) + return "", os.ErrorString("mail: missing word in phrase") + } + phrase = strings.Join(words, " ") + return phrase, nil +} + +// consumeQuotedString parses the quoted string at the start of p. +func (p *addrParser) consumeQuotedString() (qs string, err os.Error) { + // Assume first byte is '"'. + i := 1 + qsb := make([]byte, 0, 10) +Loop: + for { + if i >= p.len() { + return "", os.ErrorString("mail: unclosed quoted-string") + } + switch c := (*p)[i]; { + case c == '"': + break Loop + case c == '\\': + if i+1 == p.len() { + return "", os.ErrorString("mail: unclosed quoted-string") + } + qsb = append(qsb, (*p)[i+1]) + i += 2 + case isQtext(c), c == ' ' || c == '\t': + // qtext (printable US-ASCII excluding " and \), or + // FWS (almost; we're ignoring CRLF) + qsb = append(qsb, c) + i++ + default: + return "", fmt.Errorf("mail: bad character in quoted-string: %q", c) + } + } + *p = (*p)[i+1:] + return string(qsb), nil +} + +// consumeAtom parses an RFC 5322 atom at the start of p. +// If dot is true, consumeAtom parses an RFC 5322 dot-atom instead. +func (p *addrParser) consumeAtom(dot bool) (atom string, err os.Error) { + if !isAtext(p.peek(), false) { + return "", os.ErrorString("mail: invalid string") + } + i := 1 + for ; i < p.len() && isAtext((*p)[i], dot); i++ { + } + // TODO(dsymonds): Remove the []byte() conversion here when 6g doesn't need it. + atom, *p = string([]byte((*p)[:i])), (*p)[i:] + return atom, nil +} + +func (p *addrParser) consume(c byte) bool { + if p.empty() || p.peek() != c { + return false + } + *p = (*p)[1:] + return true +} + +// skipSpace skips the leading space and tab characters. +func (p *addrParser) skipSpace() { + *p = bytes.TrimLeft(*p, " \t") +} + +func (p *addrParser) peek() byte { + return (*p)[0] +} + +func (p *addrParser) empty() bool { + return p.len() == 0 +} + +func (p *addrParser) len() int { + return len(*p) +} + +func decodeRFC2047Word(s string) (string, os.Error) { + fields := strings.Split(s, "?", -1) + if len(fields) != 5 || fields[0] != "=" || fields[4] != "=" { + return "", os.ErrorString("mail: address not RFC 2047 encoded") + } + charset, enc := strings.ToLower(fields[1]), strings.ToLower(fields[2]) + // TODO(dsymonds): Support "b" encoding too. + if enc != "q" { + return "", fmt.Errorf("mail: RFC 2047 encoding not supported: %q", enc) + } + if charset != "iso-8859-1" && charset != "utf-8" { + return "", fmt.Errorf("mail: charset not supported: %q", charset) + } + + in := fields[3] + b := new(bytes.Buffer) + for i := 0; i < len(in); i++ { + switch c := in[i]; { + case c == '=' && i+2 < len(in): + x, err := strconv.Btoi64(in[i+1:i+3], 16) + if err != nil { + return "", fmt.Errorf("mail: invalid RFC 2047 encoding: %q", in[i:i+3]) + } + i += 2 + switch charset { + case "iso-8859-1": + b.WriteRune(int(x)) + case "utf-8": + b.WriteByte(byte(x)) + } + case c == '_': + b.WriteByte(' ') + default: + b.WriteByte(c) + } + } + return b.String(), nil +} + +var atextChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + "abcdefghijklmnopqrstuvwxyz" + + "0123456789" + + "!#$%&'*+-/=?^_`{|}~") + +// isAtext returns true if c is an RFC 5322 atext character. +// If dot is true, period is included. +func isAtext(c byte, dot bool) bool { + if dot && c == '.' { + return true + } + return bytes.IndexByte(atextChars, c) >= 0 +} + +// isQtext returns true if c is an RFC 5322 qtest character. +func isQtext(c byte) bool { + // Printable US-ASCII, excluding backslash or quote. + if c == '\\' || c == '"' { + return false + } + return '!' <= c && c <= '~' +} + +// isVchar returns true if c is an RFC 5322 VCHAR character. +func isVchar(c byte) bool { + // Visible (printing) characters. + return '!' <= c && c <= '~' +} diff --git a/src/pkg/mail/message_test.go b/src/pkg/mail/message_test.go index 1d1c6352e..1ff45d2c1 100644 --- a/src/pkg/mail/message_test.go +++ b/src/pkg/mail/message_test.go @@ -127,3 +127,132 @@ func TestDateParsing(t *testing.T) { } } } + +func TestAddressParsing(t *testing.T) { + tests := []struct { + addrsStr string + exp []*Address + }{ + // Bare address + { + `jdoe@machine.example`, + []*Address{&Address{ + Address: "jdoe@machine.example", + }}, + }, + // RFC 5322, Appendix A.1.1 + { + `John Doe <jdoe@machine.example>`, + []*Address{&Address{ + Name: "John Doe", + Address: "jdoe@machine.example", + }}, + }, + // RFC 5322, Appendix A.1.2 + { + `"Joe Q. Public" <john.q.public@example.com>`, + []*Address{&Address{ + Name: "Joe Q. Public", + Address: "john.q.public@example.com", + }}, + }, + { + `Mary Smith <mary@x.test>, jdoe@example.org, Who? <one@y.test>`, + []*Address{ + &Address{ + Name: "Mary Smith", + Address: "mary@x.test", + }, + &Address{ + Address: "jdoe@example.org", + }, + &Address{ + Name: "Who?", + Address: "one@y.test", + }, + }, + }, + { + `<boss@nil.test>, "Giant; \"Big\" Box" <sysservices@example.net>`, + []*Address{ + &Address{ + Address: "boss@nil.test", + }, + &Address{ + Name: `Giant; "Big" Box`, + Address: "sysservices@example.net", + }, + }, + }, + // RFC 5322, Appendix A.1.3 + // TODO(dsymonds): Group addresses. + + // RFC 2047 "Q"-encoded ISO-8859-1 address. + { + `=?iso-8859-1?q?J=F6rg_Doe?= <joerg@example.com>`, + []*Address{ + &Address{ + Name: `Jörg Doe`, + Address: "joerg@example.com", + }, + }, + }, + // RFC 2047 "Q"-encoded UTF-8 address. + { + `=?utf-8?q?J=C3=B6rg_Doe?= <joerg@example.com>`, + []*Address{ + &Address{ + Name: `Jörg Doe`, + Address: "joerg@example.com", + }, + }, + }, + // RFC 2047, Section 8. + { + `=?ISO-8859-1?Q?Andr=E9?= Pirard <PIRARD@vm1.ulg.ac.be>`, + []*Address{ + &Address{ + Name: `André Pirard`, + Address: "PIRARD@vm1.ulg.ac.be", + }, + }, + }, + } + for _, test := range tests { + addrs, err := newAddrParser(test.addrsStr).parseAddressList() + if err != nil { + t.Errorf("Failed parsing %q: %v", test.addrsStr, err) + continue + } + if !reflect.DeepEqual(addrs, test.exp) { + t.Errorf("Parse of %q: got %+v, want %+v", test.addrsStr, addrs, test.exp) + } + } +} + +func TestAddressFormatting(t *testing.T) { + tests := []struct { + addr *Address + exp string + }{ + { + &Address{Address: "bob@example.com"}, + "<bob@example.com>", + }, + { + &Address{Name: "Bob", Address: "bob@example.com"}, + `"Bob" <bob@example.com>`, + }, + { + // note the ö (o with an umlaut) + &Address{Name: "Böb", Address: "bob@example.com"}, + `=?utf-8?q?B=C3=B6b?= <bob@example.com>`, + }, + } + for _, test := range tests { + s := test.addr.String() + if s != test.exp { + t.Errorf("Address%+v.String() = %v, want %v", *test.addr, s, test.exp) + } + } +} diff --git a/src/pkg/math/Makefile b/src/pkg/math/Makefile index 71347b7fa..8e8e74ae4 100644 --- a/src/pkg/math/Makefile +++ b/src/pkg/math/Makefile @@ -6,6 +6,9 @@ include ../../Make.inc TARG=math +OFILES_arm=\ + sqrt_arm.$O\ + OFILES_amd64=\ exp_amd64.$O\ fabs_amd64.$O\ diff --git a/src/pkg/math/sqrt_arm.s b/src/pkg/math/sqrt_arm.s new file mode 100644 index 000000000..befbb8a89 --- /dev/null +++ b/src/pkg/math/sqrt_arm.s @@ -0,0 +1,10 @@ +// Copyright 2011 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. + +// func Sqrt(x float64) float64 +TEXT ·Sqrt(SB),7,$0 + MOVD x+0(FP),F0 + SQRTD F0,F0 + MOVD F0,r+8(FP) + RET diff --git a/src/pkg/math/sqrt_port.go b/src/pkg/math/sqrt_port.go index 83af255bf..148239bcf 100644 --- a/src/pkg/math/sqrt_port.go +++ b/src/pkg/math/sqrt_port.go @@ -141,3 +141,7 @@ func sqrtGo(x float64) float64 { ix = q>>1 + uint64(exp-1+bias)<<shift // significand + biased exponent return Float64frombits(ix) } + +func sqrtGoC(f float64, r *float64) { + *r = sqrtGo(f) +} diff --git a/src/pkg/net/Makefile b/src/pkg/net/Makefile index d4adbffc0..5472df392 100644 --- a/src/pkg/net/Makefile +++ b/src/pkg/net/Makefile @@ -10,6 +10,7 @@ GOFILES=\ dnsmsg.go\ fd_$(GOOS).go\ hosts.go\ + interface.go\ ip.go\ ipsock.go\ iprawsock.go\ @@ -27,6 +28,7 @@ GOFILES_freebsd=\ dnsconfig.go\ fd.go\ file.go\ + interface_bsd.go\ newpollserver.go\ port.go\ sendfile_stub.go\ @@ -41,6 +43,7 @@ GOFILES_darwin=\ dnsconfig.go\ fd.go\ file.go\ + interface_bsd.go\ newpollserver.go\ port.go\ sendfile_stub.go\ @@ -55,12 +58,14 @@ GOFILES_linux=\ dnsconfig.go\ fd.go\ file.go\ + interface_linux.go\ newpollserver.go\ port.go\ sendfile_linux.go\ sock_linux.go\ GOFILES_plan9=\ + interface_stub.go\ sendfile_stub.go\ ifeq ($(GOARCH),arm) @@ -75,6 +80,7 @@ endif GOFILES_windows=\ cgo_stub.go\ file_windows.go\ + interface_stub.go\ resolv_windows.go\ sendfile_stub.go\ sock_windows.go\ diff --git a/src/pkg/net/interface.go b/src/pkg/net/interface.go new file mode 100644 index 000000000..f622487ab --- /dev/null +++ b/src/pkg/net/interface.go @@ -0,0 +1,89 @@ +// Copyright 2011 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. + +// Network interface identification + +package net + +import ( + "bytes" + "fmt" + "os" +) + +// A HardwareAddr represents a physical hardware address. +type HardwareAddr []byte + +func (a HardwareAddr) String() string { + var buf bytes.Buffer + for i, b := range a { + if i > 0 { + buf.WriteByte(':') + } + fmt.Fprintf(&buf, "%02x", b) + } + return buf.String() +} + +// Interface represents a mapping between network interface name +// and index. It also represents network interface facility +// information. +type Interface struct { + Index int // positive integer that starts at one, zero is never used + MTU int // maximum transmission unit + Name string // e.g., "en0", "lo0", "eth0.100" + HardwareAddr HardwareAddr // IEEE MAC-48, EUI-48 and EUI-64 form + rawFlags int +} + +// Addrs returns interface addresses for a specific interface. +func (ifi *Interface) Addrs() ([]Addr, os.Error) { + if ifi == nil { + return nil, os.NewError("net: invalid interface") + } + return interfaceAddrTable(ifi.Index) +} + +// Interfaces returns a list of the systems's network interfaces. +func Interfaces() ([]Interface, os.Error) { + return interfaceTable(0) +} + +// InterfaceAddrs returns a list of the system's network interface +// addresses. +func InterfaceAddrs() ([]Addr, os.Error) { + return interfaceAddrTable(0) +} + +// InterfaceByIndex returns the interface specified by index. +func InterfaceByIndex(index int) (*Interface, os.Error) { + if index <= 0 { + return nil, os.NewError("net: invalid interface index") + } + ift, err := interfaceTable(index) + if err != nil { + return nil, err + } + for _, ifi := range ift { + return &ifi, nil + } + return nil, os.NewError("net: no such interface") +} + +// InterfaceByName returns the interface specified by name. +func InterfaceByName(name string) (*Interface, os.Error) { + if name == "" { + return nil, os.NewError("net: invalid interface name") + } + ift, err := interfaceTable(0) + if err != nil { + return nil, err + } + for _, ifi := range ift { + if name == ifi.Name { + return &ifi, nil + } + } + return nil, os.NewError("net: no such interface") +} diff --git a/src/pkg/net/interface_bsd.go b/src/pkg/net/interface_bsd.go new file mode 100644 index 000000000..141b95b38 --- /dev/null +++ b/src/pkg/net/interface_bsd.go @@ -0,0 +1,195 @@ +// Copyright 2011 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. + +// Network interface identification for BSD variants + +package net + +import ( + "os" + "syscall" + "unsafe" +) + +// IsUp returns true if ifi is up. +func (ifi *Interface) IsUp() bool { + if ifi == nil { + return false + } + return ifi.rawFlags&syscall.IFF_UP != 0 +} + +// IsLoopback returns true if ifi is a loopback interface. +func (ifi *Interface) IsLoopback() bool { + if ifi == nil { + return false + } + return ifi.rawFlags&syscall.IFF_LOOPBACK != 0 +} + +// CanBroadcast returns true if ifi supports a broadcast access +// capability. +func (ifi *Interface) CanBroadcast() bool { + if ifi == nil { + return false + } + return ifi.rawFlags&syscall.IFF_BROADCAST != 0 +} + +// IsPointToPoint returns true if ifi belongs to a point-to-point +// link. +func (ifi *Interface) IsPointToPoint() bool { + if ifi == nil { + return false + } + return ifi.rawFlags&syscall.IFF_POINTOPOINT != 0 +} + +// CanMulticast returns true if ifi supports a multicast access +// capability. +func (ifi *Interface) CanMulticast() bool { + if ifi == nil { + return false + } + return ifi.rawFlags&syscall.IFF_MULTICAST != 0 +} + +// If the ifindex is zero, interfaceTable returns mappings of all +// network interfaces. Otheriwse it returns a mapping of a specific +// interface. +func interfaceTable(ifindex int) ([]Interface, os.Error) { + var ( + tab []byte + e int + msgs []syscall.RoutingMessage + ift []Interface + ) + + tab, e = syscall.RouteRIB(syscall.NET_RT_IFLIST, ifindex) + if e != 0 { + return nil, os.NewSyscallError("route rib", e) + } + + msgs, e = syscall.ParseRoutingMessage(tab) + if e != 0 { + return nil, os.NewSyscallError("route message", e) + } + + for _, m := range msgs { + switch v := m.(type) { + case *syscall.InterfaceMessage: + if ifindex == 0 || ifindex == int(v.Header.Index) { + ifi, err := newLink(v) + if err != nil { + return nil, err + } + ift = append(ift, ifi...) + } + } + } + + return ift, nil +} + +func newLink(m *syscall.InterfaceMessage) ([]Interface, os.Error) { + var ift []Interface + + sas, e := syscall.ParseRoutingSockaddr(m) + if e != 0 { + return nil, os.NewSyscallError("route sockaddr", e) + } + + for _, s := range sas { + switch v := s.(type) { + case *syscall.SockaddrDatalink: + // NOTE: SockaddrDatalink.Data is minimum work area, + // can be larger. + m.Data = m.Data[unsafe.Offsetof(v.Data):] + ifi := Interface{Index: int(m.Header.Index), rawFlags: int(m.Header.Flags)} + var name [syscall.IFNAMSIZ]byte + for i := 0; i < int(v.Nlen); i++ { + name[i] = byte(m.Data[i]) + } + ifi.Name = string(name[:v.Nlen]) + ifi.MTU = int(m.Header.Data.Mtu) + addr := make([]byte, v.Alen) + for i := 0; i < int(v.Alen); i++ { + addr[i] = byte(m.Data[int(v.Nlen)+i]) + } + ifi.HardwareAddr = addr[:v.Alen] + ift = append(ift, ifi) + } + } + + return ift, nil +} + +// If the ifindex is zero, interfaceAddrTable returns addresses +// for all network interfaces. Otherwise it returns addresses +// for a specific interface. +func interfaceAddrTable(ifindex int) ([]Addr, os.Error) { + var ( + tab []byte + e int + msgs []syscall.RoutingMessage + ifat []Addr + ) + + tab, e = syscall.RouteRIB(syscall.NET_RT_IFLIST, ifindex) + if e != 0 { + return nil, os.NewSyscallError("route rib", e) + } + + msgs, e = syscall.ParseRoutingMessage(tab) + if e != 0 { + return nil, os.NewSyscallError("route message", e) + } + + for _, m := range msgs { + switch v := m.(type) { + case *syscall.InterfaceAddrMessage: + if ifindex == 0 || ifindex == int(v.Header.Index) { + ifa, err := newAddr(v) + if err != nil { + return nil, err + } + ifat = append(ifat, ifa...) + } + } + } + + return ifat, nil +} + +func newAddr(m *syscall.InterfaceAddrMessage) ([]Addr, os.Error) { + var ifat []Addr + + sas, e := syscall.ParseRoutingSockaddr(m) + if e != 0 { + return nil, os.NewSyscallError("route sockaddr", e) + } + + for _, s := range sas { + var ifa IPAddr + switch v := s.(type) { + case *syscall.SockaddrInet4: + ifa.IP = IPv4(v.Addr[0], v.Addr[1], v.Addr[2], v.Addr[3]) + case *syscall.SockaddrInet6: + ifa.IP = make(IP, IPv6len) + copy(ifa.IP, v.Addr[:]) + // NOTE: KAME based IPv6 protcol stack usually embeds + // the interface index in the interface-local or link- + // local address as the kernel-internal form. + if ifa.IP.IsLinkLocalUnicast() || + ifa.IP.IsInterfaceLocalMulticast() || + ifa.IP.IsLinkLocalMulticast() { + // remove embedded scope zone ID + ifa.IP[2], ifa.IP[3] = 0, 0 + } + } + ifat = append(ifat, ifa.toAddr()) + } + + return ifat, nil +} diff --git a/src/pkg/net/interface_linux.go b/src/pkg/net/interface_linux.go new file mode 100644 index 000000000..5c9657834 --- /dev/null +++ b/src/pkg/net/interface_linux.go @@ -0,0 +1,208 @@ +// Copyright 2011 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. + +// Network interface identification for Linux + +package net + +import ( + "os" + "syscall" + "unsafe" +) + +// IsUp returns true if ifi is up. +func (ifi *Interface) IsUp() bool { + if ifi == nil { + return false + } + return ifi.rawFlags&syscall.IFF_UP != 0 +} + +// IsLoopback returns true if ifi is a loopback interface. +func (ifi *Interface) IsLoopback() bool { + if ifi == nil { + return false + } + return ifi.rawFlags&syscall.IFF_LOOPBACK != 0 +} + +// CanBroadcast returns true if ifi supports a broadcast access +// capability. +func (ifi *Interface) CanBroadcast() bool { + if ifi == nil { + return false + } + return ifi.rawFlags&syscall.IFF_BROADCAST != 0 +} + +// IsPointToPoint returns true if ifi belongs to a point-to-point +// link. +func (ifi *Interface) IsPointToPoint() bool { + if ifi == nil { + return false + } + return ifi.rawFlags&syscall.IFF_POINTOPOINT != 0 +} + +// CanMulticast returns true if ifi supports a multicast access +// capability. +func (ifi *Interface) CanMulticast() bool { + if ifi == nil { + return false + } + return ifi.rawFlags&syscall.IFF_MULTICAST != 0 +} + +// If the ifindex is zero, interfaceTable returns mappings of all +// network interfaces. Otheriwse it returns a mapping of a specific +// interface. +func interfaceTable(ifindex int) ([]Interface, os.Error) { + var ( + ift []Interface + tab []byte + msgs []syscall.NetlinkMessage + e int + ) + + tab, e = syscall.NetlinkRIB(syscall.RTM_GETLINK, syscall.AF_UNSPEC) + if e != 0 { + return nil, os.NewSyscallError("netlink rib", e) + } + + msgs, e = syscall.ParseNetlinkMessage(tab) + if e != 0 { + return nil, os.NewSyscallError("netlink message", e) + } + + for _, m := range msgs { + switch m.Header.Type { + case syscall.NLMSG_DONE: + goto done + case syscall.RTM_NEWLINK: + ifim := (*syscall.IfInfomsg)(unsafe.Pointer(&m.Data[0])) + if ifindex == 0 || ifindex == int(ifim.Index) { + attrs, e := syscall.ParseNetlinkRouteAttr(&m) + if e != 0 { + return nil, os.NewSyscallError("netlink routeattr", e) + } + ifi := newLink(attrs, ifim) + ift = append(ift, ifi) + } + } + } + +done: + return ift, nil +} + +func newLink(attrs []syscall.NetlinkRouteAttr, ifim *syscall.IfInfomsg) Interface { + ifi := Interface{Index: int(ifim.Index), rawFlags: int(ifim.Flags)} + for _, a := range attrs { + switch a.Attr.Type { + case syscall.IFLA_ADDRESS: + var nonzero bool + for _, b := range a.Value { + if b != 0 { + nonzero = true + } + } + if nonzero { + ifi.HardwareAddr = a.Value[:] + } + case syscall.IFLA_IFNAME: + ifi.Name = string(a.Value[:]) + case syscall.IFLA_MTU: + ifi.MTU = int(uint32(a.Value[3])<<24 | uint32(a.Value[2])<<16 | uint32(a.Value[1])<<8 | uint32(a.Value[0])) + } + } + return ifi +} + +// If the ifindex is zero, interfaceAddrTable returns addresses +// for all network interfaces. Otherwise it returns addresses +// for a specific interface. +func interfaceAddrTable(ifindex int) ([]Addr, os.Error) { + var ( + ifat4 []Addr + ifat6 []Addr + tab []byte + msgs4 []syscall.NetlinkMessage + msgs6 []syscall.NetlinkMessage + e int + err os.Error + ) + + tab, e = syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_INET) + if e != 0 { + return nil, os.NewSyscallError("netlink rib", e) + } + msgs4, e = syscall.ParseNetlinkMessage(tab) + if e != 0 { + return nil, os.NewSyscallError("netlink message", e) + } + ifat4, err = addrTable(msgs4, ifindex) + if err != nil { + return nil, err + } + + tab, e = syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_INET6) + if e != 0 { + return nil, os.NewSyscallError("netlink rib", e) + } + msgs6, e = syscall.ParseNetlinkMessage(tab) + if e != 0 { + return nil, os.NewSyscallError("netlink message", e) + } + ifat6, err = addrTable(msgs6, ifindex) + if err != nil { + return nil, err + } + + return append(ifat4, ifat6...), nil +} + +func addrTable(msgs []syscall.NetlinkMessage, ifindex int) ([]Addr, os.Error) { + var ifat []Addr + + for _, m := range msgs { + switch m.Header.Type { + case syscall.NLMSG_DONE: + goto done + case syscall.RTM_NEWADDR: + ifam := (*syscall.IfAddrmsg)(unsafe.Pointer(&m.Data[0])) + if ifindex == 0 || ifindex == int(ifam.Index) { + attrs, e := syscall.ParseNetlinkRouteAttr(&m) + if e != 0 { + return nil, os.NewSyscallError("netlink routeattr", e) + } + ifat = append(ifat, newAddr(attrs, int(ifam.Family))...) + } + } + } + +done: + return ifat, nil +} + +func newAddr(attrs []syscall.NetlinkRouteAttr, family int) []Addr { + var ifat []Addr + + for _, a := range attrs { + switch a.Attr.Type { + case syscall.IFA_ADDRESS: + ifa := IPAddr{} + switch family { + case syscall.AF_INET: + ifa.IP = IPv4(a.Value[0], a.Value[1], a.Value[2], a.Value[3]) + case syscall.AF_INET6: + ifa.IP = make(IP, IPv6len) + copy(ifa.IP, a.Value[:]) + } + ifat = append(ifat, ifa.toAddr()) + } + } + + return ifat +} diff --git a/src/pkg/net/interface_stub.go b/src/pkg/net/interface_stub.go new file mode 100644 index 000000000..feb871bb5 --- /dev/null +++ b/src/pkg/net/interface_stub.go @@ -0,0 +1,51 @@ +// Copyright 2011 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. + +// Network interface identification + +package net + +import "os" + +// IsUp returns true if ifi is up. +func (ifi *Interface) IsUp() bool { + return false +} + +// IsLoopback returns true if ifi is a loopback interface. +func (ifi *Interface) IsLoopback() bool { + return false +} + +// CanBroadcast returns true if ifi supports a broadcast access +// capability. +func (ifi *Interface) CanBroadcast() bool { + return false +} + +// IsPointToPoint returns true if ifi belongs to a point-to-point +// link. +func (ifi *Interface) IsPointToPoint() bool { + return false +} + +// CanMulticast returns true if ifi supports a multicast access +// capability. +func (ifi *Interface) CanMulticast() bool { + return false +} + +// If the ifindex is zero, interfaceTable returns mappings of all +// network interfaces. Otheriwse it returns a mapping of a specific +// interface. +func interfaceTable(ifindex int) ([]Interface, os.Error) { + return nil, nil +} + +// If the ifindex is zero, interfaceAddrTable returns addresses +// for all network interfaces. Otherwise it returns addresses +// for a specific interface. +func interfaceAddrTable(ifindex int) ([]Addr, os.Error) { + return nil, nil +} diff --git a/src/pkg/net/interface_test.go b/src/pkg/net/interface_test.go new file mode 100644 index 000000000..938434623 --- /dev/null +++ b/src/pkg/net/interface_test.go @@ -0,0 +1,90 @@ +// Copyright 2011 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 net + +import ( + "bytes" + "testing" +) + +func sameInterface(i, j *Interface) bool { + if i == nil || j == nil { + return false + } + if i.Index == j.Index && i.Name == j.Name && bytes.Equal(i.HardwareAddr, j.HardwareAddr) { + return true + } + return false +} + +func interfaceFlagsString(ifi *Interface) string { + fs := "<" + if ifi.IsUp() { + fs += "UP," + } + if ifi.CanBroadcast() { + fs += "BROADCAST," + } + if ifi.IsLoopback() { + fs += "LOOPBACK," + } + if ifi.IsPointToPoint() { + fs += "POINTOPOINT," + } + if ifi.CanMulticast() { + fs += "MULTICAST," + } + if len(fs) > 1 { + fs = fs[:len(fs)-1] + } + fs += ">" + return fs +} + +func TestInterfaces(t *testing.T) { + ift, err := Interfaces() + if err != nil { + t.Fatalf("Interfaces() failed: %v", err) + } + t.Logf("table: len/cap = %v/%v\n", len(ift), cap(ift)) + + for _, ifi := range ift { + ifxi, err := InterfaceByIndex(ifi.Index) + if err != nil { + t.Fatalf("InterfaceByIndex(%#q) failed: %v", ifi.Index, err) + } + if !sameInterface(ifxi, &ifi) { + t.Fatalf("InterfaceByIndex(%#q) = %v, want %v", ifi.Index, *ifxi, ifi) + } + ifxn, err := InterfaceByName(ifi.Name) + if err != nil { + t.Fatalf("InterfaceByName(%#q) failed: %v", ifi.Name, err) + } + if !sameInterface(ifxn, &ifi) { + t.Fatalf("InterfaceByName(%#q) = %v, want %v", ifi.Name, *ifxn, ifi) + } + ifat, err := ifi.Addrs() + if err != nil { + t.Fatalf("Interface.Addrs() failed: %v", err) + } + t.Logf("%s: flags %s, ifindex %v, mtu %v\n", ifi.Name, interfaceFlagsString(&ifi), ifi.Index, ifi.MTU) + for _, ifa := range ifat { + t.Logf("\tinterface address %s\n", ifa.String()) + } + t.Logf("\thardware address %v", ifi.HardwareAddr.String()) + } +} + +func TestInterfaceAddrs(t *testing.T) { + ifat, err := InterfaceAddrs() + if err != nil { + t.Fatalf("InterfaceAddrs() failed: %v", err) + } + t.Logf("table: len/cap = %v/%v\n", len(ifat), cap(ifat)) + + for _, ifa := range ifat { + t.Logf("interface address %s\n", ifa.String()) + } +} diff --git a/src/pkg/netchan/import.go b/src/pkg/netchan/import.go index 0a700ca2b..7d96228c4 100644 --- a/src/pkg/netchan/import.go +++ b/src/pkg/netchan/import.go @@ -11,6 +11,7 @@ import ( "os" "reflect" "sync" + "time" ) // Import @@ -31,6 +32,9 @@ type Importer struct { chans map[int]*netChan errors chan os.Error maxId int + mu sync.Mutex // protects remaining fields + unacked int64 // number of unacknowledged sends. + seqLock sync.Mutex // guarantees messages are in sequence, only locked under mu } // NewImporter creates a new Importer object to import a set of channels @@ -42,6 +46,7 @@ func NewImporter(conn io.ReadWriter) *Importer { imp.chans = make(map[int]*netChan) imp.names = make(map[string]*netChan) imp.errors = make(chan os.Error, 10) + imp.unacked = 0 go imp.run() return imp } @@ -80,8 +85,10 @@ func (imp *Importer) run() { for { *hdr = header{} if e := imp.decode(hdrValue); e != nil { - impLog("header:", e) - imp.shutdown() + if e != os.EOF { + impLog("header:", e) + imp.shutdown() + } return } switch hdr.PayloadType { @@ -114,6 +121,9 @@ func (imp *Importer) run() { nch := imp.getChan(hdr.Id, true) if nch != nil { nch.acked() + imp.mu.Lock() + imp.unacked-- + imp.mu.Unlock() } continue default: @@ -220,10 +230,17 @@ func (imp *Importer) ImportNValues(name string, chT interface{}, dir Dir, size, } return } + // We hold the lock during transmission to guarantee messages are + // sent in order. + imp.mu.Lock() + imp.unacked++ + imp.seqLock.Lock() + imp.mu.Unlock() if err = imp.encode(hdr, payData, val.Interface()); err != nil { impLog("error encoding client send:", err) return } + imp.seqLock.Unlock() } }() } @@ -244,3 +261,27 @@ func (imp *Importer) Hangup(name string) os.Error { nc.close() return nil } + +func (imp *Importer) unackedCount() int64 { + imp.mu.Lock() + n := imp.unacked + imp.mu.Unlock() + return n +} + +// Drain waits until all messages sent from this exporter/importer, including +// those not yet sent to any server and possibly including those sent while +// Drain was executing, have been received by the exporter. In short, it +// waits until all the importer's messages have been received. +// If the timeout (measured in nanoseconds) is positive and Drain takes +// longer than that to complete, an error is returned. +func (imp *Importer) Drain(timeout int64) os.Error { + startTime := time.Nanoseconds() + for imp.unackedCount() > 0 { + if timeout > 0 && time.Nanoseconds()-startTime >= timeout { + return os.ErrorString("timeout") + } + time.Sleep(100 * 1e6) + } + return nil +} diff --git a/src/pkg/netchan/netchan_test.go b/src/pkg/netchan/netchan_test.go index fd4d8f780..8c0f9a6e4 100644 --- a/src/pkg/netchan/netchan_test.go +++ b/src/pkg/netchan/netchan_test.go @@ -178,6 +178,16 @@ func TestExportDrain(t *testing.T) { <-done } +// Not a great test but it does at least invoke Drain. +func TestImportDrain(t *testing.T) { + exp, imp := pair(t) + expDone := make(chan bool) + go exportReceive(exp, t, expDone) + <-expDone + importSend(imp, closeCount, t, nil) + imp.Drain(0) +} + // Not a great test but it does at least invoke Sync. func TestExportSync(t *testing.T) { exp, imp := pair(t) diff --git a/src/pkg/os/Makefile b/src/pkg/os/Makefile index c781df7af..497e5a958 100644 --- a/src/pkg/os/Makefile +++ b/src/pkg/os/Makefile @@ -27,6 +27,7 @@ GOFILES_freebsd=\ sys_bsd.go\ exec_posix.go\ exec_unix.go\ + signal_unix.go\ GOFILES_darwin=\ dir_unix.go\ @@ -38,6 +39,7 @@ GOFILES_darwin=\ sys_bsd.go\ exec_posix.go\ exec_unix.go\ + signal_unix.go\ GOFILES_linux=\ dir_unix.go\ @@ -49,6 +51,7 @@ GOFILES_linux=\ sys_linux.go\ exec_posix.go\ exec_unix.go\ + signal_unix.go\ GOFILES_windows=\ dir_windows.go\ @@ -60,6 +63,7 @@ GOFILES_windows=\ sys_windows.go\ exec_posix.go\ exec_windows.go\ + signal_windows.go\ GOFILES_plan9=\ dir_plan9.go\ @@ -72,4 +76,12 @@ GOFILES_plan9=\ GOFILES+=$(GOFILES_$(GOOS)) +CLEANFILES+=signal_unix.go signal_windows.go + include ../../Make.pkg + +signal_unix.go: ../syscall/zerrors_$(GOOS)_$(GOARCH).go + ./mkunixsignals.sh $< > $@ || rm -f $@ + +signal_windows.go: ../syscall/ztypes_$(GOOS)_$(GOARCH).go + ./mkunixsignals.sh $< > $@ || rm -f $@ diff --git a/src/pkg/os/env_plan9.go b/src/pkg/os/env_plan9.go index 14df55ed0..1fed89f92 100644 --- a/src/pkg/os/env_plan9.go +++ b/src/pkg/os/env_plan9.go @@ -23,13 +23,18 @@ func Getenverror(key string) (value string, err Error) { } defer f.Close() - var buf [4096]byte - n, e := f.Read(buf[:len(buf)-1]) + l, _ := f.Seek(0, 2) + f.Seek(0, 0) + buf := make([]byte, l) + n, e := f.Read(buf) if iserror(e) { return "", ENOENV } - buf[n] = 0 - return string(buf[0:n]), nil + + if n > 0 && buf[n-1] == 0 { + buf = buf[:n-1] + } + return string(buf), nil } // Getenv retrieves the value of the environment variable named by the key. @@ -52,7 +57,7 @@ func Setenv(key, value string) Error { } defer f.Close() - _, e = f.Write(syscall.StringByteSlice(value)) + _, e = f.Write([]byte(value)) return nil } diff --git a/src/pkg/os/error_posix.go b/src/pkg/os/error_posix.go index 0ee34e4b0..d43f1786d 100644 --- a/src/pkg/os/error_posix.go +++ b/src/pkg/os/error_posix.go @@ -13,7 +13,7 @@ type Errno int64 func (e Errno) String() string { return syscall.Errstr(int(e)) } func (e Errno) Temporary() bool { - return e == Errno(syscall.EINTR) || e.Timeout() + return e == Errno(syscall.EINTR) || e == Errno(syscall.EMFILE) || e.Timeout() } func (e Errno) Timeout() bool { diff --git a/src/pkg/os/exec_posix.go b/src/pkg/os/exec_posix.go index 9102dc0a4..bf992ef42 100644 --- a/src/pkg/os/exec_posix.go +++ b/src/pkg/os/exec_posix.go @@ -4,7 +4,25 @@ package os -import "syscall" +import ( + "runtime" + "syscall" +) + +// A Signal can represent any operating system signal. +type Signal interface { + String() string +} + +type UnixSignal int32 + +func (sig UnixSignal) String() string { + s := runtime.Signame(int32(sig)) + if len(s) > 0 { + return s + } + return "UnixSignal" +} // StartProcess starts a new process with the program, arguments and attributes // specified by name, argv and attr. @@ -34,6 +52,11 @@ func StartProcess(name string, argv []string, attr *ProcAttr) (p *Process, err E return newProcess(pid, h), nil } +// Kill causes the Process to exit immediately. +func (p *Process) Kill() Error { + return p.Signal(SIGKILL) +} + // Exec replaces the current process with an execution of the // named binary, with arguments argv and environment envv. // If successful, Exec never returns. If it fails, it returns an Error. diff --git a/src/pkg/os/exec_unix.go b/src/pkg/os/exec_unix.go index 8990d6a97..cf5ea9b61 100644 --- a/src/pkg/os/exec_unix.go +++ b/src/pkg/os/exec_unix.go @@ -45,6 +45,14 @@ func (p *Process) Wait(options int) (w *Waitmsg, err Error) { return w, nil } +// Signal sends a signal to the Process. +func (p *Process) Signal(sig Signal) Error { + if e := syscall.Kill(p.Pid, int(sig.(UnixSignal))); e != 0 { + return Errno(e) + } + return nil +} + // Release releases any resources associated with the Process. func (p *Process) Release() Error { // NOOP for unix. diff --git a/src/pkg/os/exec_windows.go b/src/pkg/os/exec_windows.go index ae8ffeab2..bac33b908 100644 --- a/src/pkg/os/exec_windows.go +++ b/src/pkg/os/exec_windows.go @@ -20,13 +20,23 @@ func (p *Process) Wait(options int) (w *Waitmsg, err Error) { return nil, ErrorString("os: unexpected result from WaitForSingleObject") } var ec uint32 - e = syscall.GetExitCodeProcess(uint32(p.handle), &ec) + e = syscall.GetExitCodeProcess(int32(p.handle), &ec) if e != 0 { return nil, NewSyscallError("GetExitCodeProcess", e) } return &Waitmsg{p.Pid, syscall.WaitStatus{s, ec}, new(syscall.Rusage)}, nil } +// Signal sends a signal to the Process. +func (p *Process) Signal(sig Signal) Error { + switch sig.(UnixSignal) { + case SIGKILL: + e := syscall.TerminateProcess(int32(p.handle), 1) + return NewSyscallError("TerminateProcess", e) + } + return Errno(syscall.EWINDOWS) +} + func (p *Process) Release() Error { if p.handle == -1 { return EINVAL diff --git a/src/pkg/os/signal/mkunix.sh b/src/pkg/os/mkunixsignals.sh index 653b01664..6ec764cbd 100755 --- a/src/pkg/os/signal/mkunix.sh +++ b/src/pkg/os/mkunixsignals.sh @@ -8,7 +8,7 @@ echo '// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT' echo cat <<EOH -package signal +package os import ( "syscall" diff --git a/src/pkg/os/signal/Makefile b/src/pkg/os/signal/Makefile index 013b91a85..26f58760e 100644 --- a/src/pkg/os/signal/Makefile +++ b/src/pkg/os/signal/Makefile @@ -7,11 +7,5 @@ include ../../../Make.inc TARG=os/signal GOFILES=\ signal.go\ - unix.go\ - -CLEANFILES+=unix.go include ../../../Make.pkg - -unix.go: ../../syscall/zerrors_$(GOOS)_$(GOARCH).go - ./mkunix.sh $< > $@ || rm -f $@ diff --git a/src/pkg/os/signal/signal.go b/src/pkg/os/signal/signal.go index 666c03e73..520f3f8a9 100644 --- a/src/pkg/os/signal/signal.go +++ b/src/pkg/os/signal/signal.go @@ -6,35 +6,20 @@ package signal import ( + "os" "runtime" - "strconv" ) -// A Signal can represent any operating system signal. -type Signal interface { - String() string -} - -type UnixSignal int32 - -func (sig UnixSignal) String() string { - s := runtime.Signame(int32(sig)) - if len(s) > 0 { - return s - } - return "Signal " + strconv.Itoa(int(sig)) -} - // Incoming is the global signal channel. // All signals received by the program will be delivered to this channel. -var Incoming <-chan Signal +var Incoming <-chan os.Signal -func process(ch chan<- Signal) { +func process(ch chan<- os.Signal) { for { var mask uint32 = runtime.Sigrecv() for sig := uint(0); sig < 32; sig++ { if mask&(1<<sig) != 0 { - ch <- UnixSignal(sig) + ch <- os.UnixSignal(sig) } } } @@ -42,7 +27,7 @@ func process(ch chan<- Signal) { func init() { runtime.Siginit() - ch := make(chan Signal) // Done here so Incoming can have type <-chan Signal + ch := make(chan os.Signal) // Done here so Incoming can have type <-chan Signal Incoming = ch go process(ch) } diff --git a/src/pkg/os/signal/signal_test.go b/src/pkg/os/signal/signal_test.go index f2679f14d..00eb29578 100644 --- a/src/pkg/os/signal/signal_test.go +++ b/src/pkg/os/signal/signal_test.go @@ -5,6 +5,7 @@ package signal import ( + "os" "syscall" "testing" ) @@ -13,7 +14,7 @@ func TestSignal(t *testing.T) { // Send this process a SIGHUP. syscall.Syscall(syscall.SYS_KILL, uintptr(syscall.Getpid()), syscall.SIGHUP, 0) - if sig := (<-Incoming).(UnixSignal); sig != SIGHUP { - t.Errorf("signal was %v, want %v", sig, SIGHUP) + if sig := (<-Incoming).(os.UnixSignal); sig != os.SIGHUP { + t.Errorf("signal was %v, want %v", sig, os.SIGHUP) } } diff --git a/src/pkg/runtime/386/memmove.s b/src/pkg/runtime/386/memmove.s index 38a0652b5..471553ba2 100644 --- a/src/pkg/runtime/386/memmove.s +++ b/src/pkg/runtime/386/memmove.s @@ -32,7 +32,6 @@ TEXT runtime·memmove(SB), 7, $0 /* * check and set for backwards - * should we look closer for overlap? */ CMPL SI, DI JLS back @@ -40,6 +39,7 @@ TEXT runtime·memmove(SB), 7, $0 /* * forward copy loop */ +forward: MOVL BX, CX SHRL $2, CX ANDL $3, BX @@ -51,10 +51,18 @@ TEXT runtime·memmove(SB), 7, $0 MOVL to+0(FP),AX RET /* + * check overlap + */ +back: + MOVL SI, CX + ADDL BX, CX + CMPL CX, DI + JLS forward +/* * whole thing backwards has * adjusted addresses */ -back: + ADDL BX, DI ADDL BX, SI STD diff --git a/src/pkg/runtime/amd64/memmove.s b/src/pkg/runtime/amd64/memmove.s index 9966b0ba7..fc9573f72 100644 --- a/src/pkg/runtime/amd64/memmove.s +++ b/src/pkg/runtime/amd64/memmove.s @@ -33,7 +33,6 @@ TEXT runtime·memmove(SB), 7, $0 /* * check and set for backwards - * should we look closer for overlap? */ CMPQ SI, DI JLS back @@ -41,6 +40,7 @@ TEXT runtime·memmove(SB), 7, $0 /* * forward copy loop */ +forward: MOVQ BX, CX SHRQ $3, CX ANDQ $7, BX @@ -51,11 +51,19 @@ TEXT runtime·memmove(SB), 7, $0 MOVQ to+0(FP),AX RET +back: +/* + * check overlap + */ + MOVQ SI, CX + ADDQ BX, CX + CMPQ CX, DI + JLS forward + /* * whole thing backwards has * adjusted addresses */ -back: ADDQ BX, DI ADDQ BX, SI STD diff --git a/src/pkg/runtime/arm/softfloat.c b/src/pkg/runtime/arm/softfloat.c index f91a6fc09..0a071dada 100644 --- a/src/pkg/runtime/arm/softfloat.c +++ b/src/pkg/runtime/arm/softfloat.c @@ -15,6 +15,7 @@ #define FLAGS_V (1 << 28) void runtime·abort(void); +void math·sqrtGoC(uint64, uint64*); static uint32 trace = 0; @@ -357,6 +358,15 @@ stage3: // regd, regm are 4bit variables regd, regm, m->freghi[regd], m->freglo[regd]); break; + case 0xeeb10bc0: // D[regd] = sqrt D[regm] + math·sqrtGoC(getd(regm), &uval); + putd(regd, uval); + + if(trace) + runtime·printf("*** D[%d] = sqrt D[%d] %x-%x\n", + regd, regm, m->freghi[regd], m->freglo[regd]); + break; + case 0xeeb40bc0: // D[regd] :: D[regm] (CMPD) runtime·fcmp64c(getd(regd), getd(regm), &cmp, &nan); m->fflag = fstatus(nan, cmp); diff --git a/src/pkg/runtime/linux/386/sys.s b/src/pkg/runtime/linux/386/sys.s index 868a0d901..e8b423324 100644 --- a/src/pkg/runtime/linux/386/sys.s +++ b/src/pkg/runtime/linux/386/sys.s @@ -47,6 +47,14 @@ TEXT runtime·setitimer(SB),7,$0-24 INT $0x80 RET +TEXT runtime·mincore(SB),7,$0-24 + MOVL $218, AX // syscall - mincore + MOVL 4(SP), BX + MOVL 8(SP), CX + MOVL 12(SP), DX + INT $0x80 + RET + TEXT runtime·gettime(SB), 7, $32 MOVL $78, AX // syscall - gettimeofday LEAL 8(SP), BX diff --git a/src/pkg/runtime/linux/amd64/sys.s b/src/pkg/runtime/linux/amd64/sys.s index eadd30005..66fdab208 100644 --- a/src/pkg/runtime/linux/amd64/sys.s +++ b/src/pkg/runtime/linux/amd64/sys.s @@ -53,6 +53,14 @@ TEXT runtime·setitimer(SB),7,$0-24 SYSCALL RET +TEXT runtime·mincore(SB),7,$0-24 + MOVQ 8(SP), DI + MOVQ 16(SP), SI + MOVQ 24(SP), DX + MOVL $27, AX // syscall entry + SYSCALL + RET + TEXT runtime·gettime(SB), 7, $32 LEAQ 8(SP), DI MOVQ $0, SI diff --git a/src/pkg/runtime/linux/arm/sys.s b/src/pkg/runtime/linux/arm/sys.s index 2b5365bd8..ab5349822 100644 --- a/src/pkg/runtime/linux/arm/sys.s +++ b/src/pkg/runtime/linux/arm/sys.s @@ -26,6 +26,7 @@ #define SYS_exit_group (SYS_BASE + 248) #define SYS_munmap (SYS_BASE + 91) #define SYS_setitimer (SYS_BASE + 104) +#define SYS_mincore (SYS_BASE + 219) #define SYS_gettid (SYS_BASE + 224) #define SYS_tkill (SYS_BASE + 238) @@ -91,6 +92,14 @@ TEXT runtime·setitimer(SB),7,$0 SWI $0 RET +TEXT runtime·mincore(SB),7,$0 + MOVW 0(FP), R0 + MOVW 4(FP), R1 + MOVW 8(FP), R2 + MOVW $SYS_mincore, R7 + SWI $0 + RET + TEXT runtime·gettime(SB),7,$32 /* dummy version - return 0,0 */ MOVW $0, R1 diff --git a/src/pkg/runtime/linux/mem.c b/src/pkg/runtime/linux/mem.c index 02f798732..38ca7e4a0 100644 --- a/src/pkg/runtime/linux/mem.c +++ b/src/pkg/runtime/linux/mem.c @@ -3,6 +3,30 @@ #include "os.h" #include "malloc.h" +enum +{ + ENOMEM = 12, +}; + +static int32 +addrspace_free(void *v, uintptr n) +{ + uintptr page_size = 4096; + uintptr off; + int8 one_byte; + + for(off = 0; off < n; off += page_size) { + int32 errval = runtime·mincore((int8 *)v + off, page_size, (void *)&one_byte); + // errval is 0 if success, or -(error_code) if error. + if (errval == 0 || errval != -ENOMEM) + return 0; + } + USED(v); + USED(n); + return 1; +} + + void* runtime·SysAlloc(uintptr n) { @@ -54,11 +78,6 @@ runtime·SysReserve(void *v, uintptr n) return p; } -enum -{ - ENOMEM = 12, -}; - void runtime·SysMap(void *v, uintptr n) { @@ -69,6 +88,11 @@ runtime·SysMap(void *v, uintptr n) // On 64-bit, we don't actually have v reserved, so tread carefully. if(sizeof(void*) == 8) { p = runtime·mmap(v, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0); + if(p != v && addrspace_free(v, n)) { + // On some systems, mmap ignores v without + // MAP_FIXED, so retry if the address space is free. + p = runtime·mmap(v, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0); + } if(p == (void*)ENOMEM) runtime·throw("runtime: out of memory"); if(p != v) { diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h index 2b2b34a3c..f3ccff1bc 100644 --- a/src/pkg/runtime/runtime.h +++ b/src/pkg/runtime/runtime.h @@ -413,6 +413,7 @@ int32 runtime·gotraceback(void); void runtime·traceback(uint8 *pc, uint8 *sp, uint8 *lr, G* gp); void runtime·tracebackothers(G*); int32 runtime·write(int32, void*, int32); +int32 runtime·mincore(void*, uintptr, byte*); bool runtime·cas(uint32*, uint32, uint32); bool runtime·casp(void**, void*, void*); uint32 runtime·xadd(uint32 volatile*, int32); diff --git a/src/pkg/runtime/windows/thread.c b/src/pkg/runtime/windows/thread.c index 2ce8fae15..81ad68033 100644 --- a/src/pkg/runtime/windows/thread.c +++ b/src/pkg/runtime/windows/thread.c @@ -373,7 +373,7 @@ runtime·compilecallback(Eface fn, bool cleanstack) return &c->asmbody; } } - if(cbs.n >= 20) + if(cbs.n >= 2000) runtime·throw("too many callback functions"); c = runtime·mal(sizeof *c + n); c->gobody = fn.data; diff --git a/src/pkg/strconv/quote.go b/src/pkg/strconv/quote.go index bbc0b2658..98b19d3a2 100644 --- a/src/pkg/strconv/quote.go +++ b/src/pkg/strconv/quote.go @@ -14,56 +14,68 @@ import ( const lowerhex = "0123456789abcdef" -func quoteWith(s string, quote byte) string { +func quoteWith(s string, quote byte, ASCIIonly bool) string { var buf bytes.Buffer buf.WriteByte(quote) - for ; len(s) > 0; s = s[1:] { - switch c := s[0]; { - case c == quote: + for width := 0; len(s) > 0; s = s[width:] { + rune := int(s[0]) + width = 1 + if rune >= utf8.RuneSelf { + rune, width = utf8.DecodeRuneInString(s) + } + if width == 1 && rune == utf8.RuneError { + goto printEscX + } + if rune == int(quote) || rune == '\\' { // always backslashed buf.WriteByte('\\') - buf.WriteByte(quote) - case c == '\\': - buf.WriteString(`\\`) - case ' ' <= c && c <= '~': - buf.WriteString(string(c)) - case c == '\a': + buf.WriteByte(byte(rune)) + continue + } + if ASCIIonly { + if rune <= unicode.MaxASCII && unicode.IsPrint(rune) { + buf.WriteRune(rune) + continue + } + } else if unicode.IsPrint(rune) { + buf.WriteRune(rune) + continue + } + switch rune { + case '\a': buf.WriteString(`\a`) - case c == '\b': + case '\b': buf.WriteString(`\b`) - case c == '\f': + case '\f': buf.WriteString(`\f`) - case c == '\n': + case '\n': buf.WriteString(`\n`) - case c == '\r': + case '\r': buf.WriteString(`\r`) - case c == '\t': + case '\t': buf.WriteString(`\t`) - case c == '\v': + case '\v': buf.WriteString(`\v`) - - case c >= utf8.RuneSelf && utf8.FullRuneInString(s): - r, size := utf8.DecodeRuneInString(s) - if r == utf8.RuneError && size == 1 { - goto EscX - } - s = s[size-1:] // next iteration will slice off 1 more - if r < 0x10000 { + default: + switch { + case rune < ' ': + printEscX: + buf.WriteString(`\x`) + buf.WriteByte(lowerhex[s[0]>>4]) + buf.WriteByte(lowerhex[s[0]&0xF]) + case rune > unicode.MaxRune: + rune = 0xFFFD + fallthrough + case rune < 0x10000: buf.WriteString(`\u`) - for j := uint(0); j < 4; j++ { - buf.WriteByte(lowerhex[(r>>(12-4*j))&0xF]) + for s := 12; s >= 0; s -= 4 { + buf.WriteByte(lowerhex[rune>>uint(s)&0xF]) } - } else { + default: buf.WriteString(`\U`) - for j := uint(0); j < 8; j++ { - buf.WriteByte(lowerhex[(r>>(28-4*j))&0xF]) + for s := 28; s >= 0; s -= 4 { + buf.WriteByte(lowerhex[rune>>uint(s)&0xF]) } } - - default: - EscX: - buf.WriteString(`\x`) - buf.WriteByte(lowerhex[c>>4]) - buf.WriteByte(lowerhex[c&0xF]) } } buf.WriteByte(quote) @@ -71,21 +83,38 @@ func quoteWith(s string, quote byte) string { } -// Quote returns a double-quoted Go string literal -// representing s. The returned string uses Go escape -// sequences (\t, \n, \xFF, \u0100) for control characters -// and non-ASCII characters. +// Quote returns a double-quoted Go string literal representing s. The +// returned string uses Go escape sequences (\t, \n, \xFF, \u0100) for +// control characters and non-printable characters as defined by +// unicode.IsPrint. func Quote(s string) string { - return quoteWith(s, '"') + return quoteWith(s, '"', false) +} + +// QuoteToASCII returns a double-quoted Go string literal representing s. +// The returned string uses Go escape sequences (\t, \n, \xFF, \u0100) for +// non-ASCII characters and non-printable characters as defined by +// unicode.IsPrint. +func QuoteToASCII(s string) string { + return quoteWith(s, '"', true) } -// QuoteRune returns a single-quoted Go character literal -// representing the rune. The returned string uses Go escape -// sequences (\t, \n, \xFF, \u0100) for control characters -// and non-ASCII characters. +// QuoteRune returns a single-quoted Go character literal representing the +// rune. The returned string uses Go escape sequences (\t, \n, \xFF, \u0100) +// for control characters and non-printable characters as defined by +// unicode.IsPrint. func QuoteRune(rune int) string { // TODO: avoid the allocation here. - return quoteWith(string(rune), '\'') + return quoteWith(string(rune), '\'', false) +} + +// QuoteRuneToASCII returns a single-quoted Go character literal representing +// the rune. The returned string uses Go escape sequences (\t, \n, \xFF, +// \u0100) for non-ASCII characters and non-printable characters as defined +// by unicode.IsPrint. +func QuoteRuneToASCII(rune int) string { + // TODO: avoid the allocation here. + return quoteWith(string(rune), '\'', true) } // CanBackquote returns whether the string s would be diff --git a/src/pkg/strconv/quote_test.go b/src/pkg/strconv/quote_test.go index 3232d611c..4d615db44 100644 --- a/src/pkg/strconv/quote_test.go +++ b/src/pkg/strconv/quote_test.go @@ -11,17 +11,18 @@ import ( ) type quoteTest struct { - in string - out string + in string + out string + ascii string } var quotetests = []quoteTest{ - {"\a\b\f\r\n\t\v", `"\a\b\f\r\n\t\v"`}, - {"\\", `"\\"`}, - {"abc\xffdef", `"abc\xffdef"`}, - {"\u263a", `"\u263a"`}, - {"\U0010ffff", `"\U0010ffff"`}, - {"\x04", `"\x04"`}, + {"\a\b\f\r\n\t\v", `"\a\b\f\r\n\t\v"`, `"\a\b\f\r\n\t\v"`}, + {"\\", `"\\"`, `"\\"`}, + {"abc\xffdef", `"abc\xffdef"`, `"abc\xffdef"`}, + {"\u263a", `"☺"`, `"\u263a"`}, + {"\U0010ffff", `"\U0010ffff"`, `"\U0010ffff"`}, + {"\x04", `"\x04"`, `"\x04"`}, } func TestQuote(t *testing.T) { @@ -32,20 +33,30 @@ func TestQuote(t *testing.T) { } } +func TestQuoteToASCII(t *testing.T) { + for _, tt := range quotetests { + if out := QuoteToASCII(tt.in); out != tt.ascii { + t.Errorf("QuoteToASCII(%s) = %s, want %s", tt.in, out, tt.ascii) + } + } +} + type quoteRuneTest struct { - in int - out string + in int + out string + ascii string } var quoterunetests = []quoteRuneTest{ - {'a', `'a'`}, - {'\a', `'\a'`}, - {'\\', `'\\'`}, - {0xFF, `'\u00ff'`}, - {0x263a, `'\u263a'`}, - {0x0010ffff, `'\U0010ffff'`}, - {0x0010ffff + 1, `'\ufffd'`}, - {0x04, `'\x04'`}, + {'a', `'a'`, `'a'`}, + {'\a', `'\a'`, `'\a'`}, + {'\\', `'\\'`, `'\\'`}, + {0xFF, `'ÿ'`, `'\u00ff'`}, + {0x263a, `'☺'`, `'\u263a'`}, + {0xfffd, `'�'`, `'\ufffd'`}, + {0x0010ffff, `'\U0010ffff'`, `'\U0010ffff'`}, + {0x0010ffff + 1, `'�'`, `'\ufffd'`}, + {0x04, `'\x04'`, `'\x04'`}, } func TestQuoteRune(t *testing.T) { @@ -56,6 +67,14 @@ func TestQuoteRune(t *testing.T) { } } +func TestQuoteRuneToASCII(t *testing.T) { + for _, tt := range quoterunetests { + if out := QuoteRuneToASCII(tt.in); out != tt.ascii { + t.Errorf("QuoteRuneToASCII(%U) = %s, want %s", tt.in, out, tt.ascii) + } + } +} + type canBackquoteTest struct { in string out bool @@ -110,7 +129,12 @@ func TestCanBackquote(t *testing.T) { } } -var unquotetests = []quoteTest{ +type unQuoteTest struct { + in string + out string +} + +var unquotetests = []unQuoteTest{ {`""`, ""}, {`"a"`, "a"}, {`"abc"`, "abc"}, diff --git a/src/pkg/sync/atomic/asm_arm.s b/src/pkg/sync/atomic/asm_arm.s index 448a98a01..95e2f5be4 100644 --- a/src/pkg/sync/atomic/asm_arm.s +++ b/src/pkg/sync/atomic/asm_arm.s @@ -90,11 +90,11 @@ add64loop: TEXT check64<>(SB),7,$16 MOVW $10, R1 // 8-aligned stack address scratch space. - MOVW $8(SP), R3 - AND $~7, R3 + MOVW $8(R13), R5 + AND $~7, R5 loop: - LDREXD (R3), R2 - STREXD R2, (R3), R0 + LDREXD (R5), R2 + STREXD R2, (R5), R0 CMP $0, R0 BEQ ok SUB $1, R1 diff --git a/src/pkg/sync/rwmutex_test.go b/src/pkg/sync/rwmutex_test.go index 9fb89f8e8..0480a6601 100644 --- a/src/pkg/sync/rwmutex_test.go +++ b/src/pkg/sync/rwmutex_test.go @@ -45,6 +45,7 @@ func doTestParallelReaders(numReaders, gomaxprocs int) { } func TestParallelReaders(t *testing.T) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1)) doTestParallelReaders(1, 4) doTestParallelReaders(3, 4) doTestParallelReaders(4, 2) @@ -102,6 +103,7 @@ func HammerRWMutex(gomaxprocs, numReaders, num_iterations int) { } func TestRWMutex(t *testing.T) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1)) n := 1000 if testing.Short() { n = 5 diff --git a/src/pkg/syscall/syscall_windows.go b/src/pkg/syscall/syscall_windows.go index bb93533bd..d01664d12 100644 --- a/src/pkg/syscall/syscall_windows.go +++ b/src/pkg/syscall/syscall_windows.go @@ -141,8 +141,9 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, errno //sys GetQueuedCompletionStatus(cphandle int32, qty *uint32, key *uint32, overlapped **Overlapped, timeout uint32) (errno int) //sys CancelIo(s uint32) (errno int) //sys CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityAttributes, threadSecurity *SecurityAttributes, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *StartupInfo, outProcInfo *ProcessInformation) (errno int) = CreateProcessW -//sys OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle uint32, errno int) -//sys GetExitCodeProcess(handle uint32, exitcode *uint32) (errno int) +//sys OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle int32, errno int) +//sys TerminateProcess(handle int32, exitcode uint32) (errno int) +//sys GetExitCodeProcess(handle int32, exitcode *uint32) (errno int) //sys GetStartupInfo(startupInfo *StartupInfo) (errno int) = GetStartupInfoW //sys GetCurrentProcess() (pseudoHandle int32, errno int) //sys DuplicateHandle(hSourceProcessHandle int32, hSourceHandle int32, hTargetProcessHandle int32, lpTargetHandle *int32, dwDesiredAccess uint32, bInheritHandle bool, dwOptions uint32) (errno int) @@ -697,10 +698,6 @@ func BindToDevice(fd int, device string) (errno int) { return // TODO(brainman): fix all needed for os -const ( - SIGTRAP = 5 -) - func Getpid() (pid int) { return -1 } func Getppid() (ppid int) { return -1 } diff --git a/src/pkg/syscall/zsyscall_windows_386.go b/src/pkg/syscall/zsyscall_windows_386.go index ce36ab6c0..447b09043 100644 --- a/src/pkg/syscall/zsyscall_windows_386.go +++ b/src/pkg/syscall/zsyscall_windows_386.go @@ -46,6 +46,7 @@ var ( procCancelIo = getSysProcAddr(modkernel32, "CancelIo") procCreateProcessW = getSysProcAddr(modkernel32, "CreateProcessW") procOpenProcess = getSysProcAddr(modkernel32, "OpenProcess") + procTerminateProcess = getSysProcAddr(modkernel32, "TerminateProcess") procGetExitCodeProcess = getSysProcAddr(modkernel32, "GetExitCodeProcess") procGetStartupInfoW = getSysProcAddr(modkernel32, "GetStartupInfoW") procGetCurrentProcess = getSysProcAddr(modkernel32, "GetCurrentProcess") @@ -542,7 +543,7 @@ func CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityA return } -func OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle uint32, errno int) { +func OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle int32, errno int) { var _p0 uint32 if inheritHandle { _p0 = 1 @@ -550,7 +551,7 @@ func OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle uint32, errn _p0 = 0 } r0, _, e1 := Syscall(procOpenProcess, 3, uintptr(da), uintptr(_p0), uintptr(pid)) - handle = uint32(r0) + handle = int32(r0) if handle == 0 { if e1 != 0 { errno = int(e1) @@ -563,7 +564,21 @@ func OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle uint32, errn return } -func GetExitCodeProcess(handle uint32, exitcode *uint32) (errno int) { +func TerminateProcess(handle int32, exitcode uint32) (errno int) { + r1, _, e1 := Syscall(procTerminateProcess, 2, uintptr(handle), uintptr(exitcode), 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func GetExitCodeProcess(handle int32, exitcode *uint32) (errno int) { r1, _, e1 := Syscall(procGetExitCodeProcess, 2, uintptr(handle), uintptr(unsafe.Pointer(exitcode)), 0) if int(r1) == 0 { if e1 != 0 { diff --git a/src/pkg/syscall/ztypes_darwin_386.go b/src/pkg/syscall/ztypes_darwin_386.go index 2dec01787..ba6e590c4 100644 --- a/src/pkg/syscall/ztypes_darwin_386.go +++ b/src/pkg/syscall/ztypes_darwin_386.go @@ -2,10 +2,6 @@ // MACHINE GENERATED - DO NOT EDIT. -// Manual corrections: TODO: need to fix godefs (issue 1466) -// change Msghdr field to Iov *Iovec (was uint32/64) -// change BpfProgram field to Insns *BpfInsn (was uint32/64) - package syscall // Constants diff --git a/src/pkg/syscall/ztypes_darwin_amd64.go b/src/pkg/syscall/ztypes_darwin_amd64.go index 96500d732..59c832812 100644 --- a/src/pkg/syscall/ztypes_darwin_amd64.go +++ b/src/pkg/syscall/ztypes_darwin_amd64.go @@ -2,10 +2,6 @@ // MACHINE GENERATED - DO NOT EDIT. -// Manual corrections: TODO: need to fix godefs (issue 1466) -// change Msghdr field to Iov *Iovec (was uint32/64) -// change BpfProgram field to Insns *BpfInsn (was uint32/64) - package syscall // Constants diff --git a/src/pkg/syscall/ztypes_windows_386.go b/src/pkg/syscall/ztypes_windows_386.go index 7b15ea404..b04fea576 100644 --- a/src/pkg/syscall/ztypes_windows_386.go +++ b/src/pkg/syscall/ztypes_windows_386.go @@ -49,6 +49,23 @@ const ( ) const ( + // More invented values for signals + SIGHUP = 0x1 + SIGINT = 0x2 + SIGQUIT = 0x3 + SIGILL = 0x4 + SIGTRAP = 0x5 + SIGABRT = 0x6 + SIGBUS = 0x7 + SIGFPE = 0x8 + SIGKILL = 0x9 + SIGSEGV = 0xb + SIGPIPE = 0xd + SIGALRM = 0xe + SIGTERM = 0xf +) + +const ( GENERIC_READ = 0x80000000 GENERIC_WRITE = 0x40000000 GENERIC_EXECUTE = 0x20000000 diff --git a/src/pkg/template/Makefile b/src/pkg/template/Makefile index 4915527b4..4f1e06527 100644 --- a/src/pkg/template/Makefile +++ b/src/pkg/template/Makefile @@ -6,7 +6,9 @@ include ../../Make.inc TARG=template GOFILES=\ + doc.go\ + execute.go\ format.go\ - template.go\ + parse.go\ include ../../Make.pkg diff --git a/src/pkg/template/doc.go b/src/pkg/template/doc.go new file mode 100644 index 000000000..e778d801d --- /dev/null +++ b/src/pkg/template/doc.go @@ -0,0 +1,91 @@ +// Copyright 2009 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 template implements data-driven templates for generating textual + output such as HTML. + + Templates are executed by applying them to a data structure. + Annotations in the template refer to elements of the data + structure (typically a field of a struct or a key in a map) + to control execution and derive values to be displayed. + The template walks the structure as it executes and the + "cursor" @ represents the value at the current location + in the structure. + + Data items may be values or pointers; the interface hides the + indirection. + + In the following, 'Field' is one of several things, according to the data. + + - The name of a field of a struct (result = data.Field), + - The value stored in a map under that key (result = data["Field"]), or + - The result of invoking a niladic single-valued method with that name + (result = data.Field()) + + If Field is a struct field or method name, it must be an exported + (capitalized) name. + + Major constructs ({} are the default delimiters for template actions; + [] are the notation in this comment for optional elements): + + {# comment } + + A one-line comment. + + {.section field} XXX [ {.or} YYY ] {.end} + + Set @ to the value of the field. It may be an explicit @ + to stay at the same point in the data. If the field is nil + or empty, execute YYY; otherwise execute XXX. + + {.repeated section field} XXX [ {.alternates with} ZZZ ] [ {.or} YYY ] {.end} + + Like .section, but field must be an array or slice. XXX + is executed for each element. If the array is nil or empty, + YYY is executed instead. If the {.alternates with} marker + is present, ZZZ is executed between iterations of XXX. + + {field} + {field1 field2 ...} + {field|formatter} + {field1 field2...|formatter} + {field|formatter1|formatter2} + + Insert the value of the fields into the output. Each field is + first looked for in the cursor, as in .section and .repeated. + If it is not found, the search continues in outer sections + until the top level is reached. + + If the field value is a pointer, leading asterisks indicate + that the value to be inserted should be evaluated through the + pointer. For example, if x.p is of type *int, {x.p} will + insert the value of the pointer but {*x.p} will insert the + value of the underlying integer. If the value is nil or not a + pointer, asterisks have no effect. + + If a formatter is specified, it must be named in the formatter + map passed to the template set up routines or in the default + set ("html","str","") and is used to process the data for + output. The formatter function has signature + func(wr io.Writer, formatter string, data ...interface{}) + where wr is the destination for output, data holds the field + values at the instantiation, and formatter is its name at + the invocation site. The default formatter just concatenates + the string representations of the fields. + + Multiple formatters separated by the pipeline character | are + executed sequentially, with each formatter receiving the bytes + emitted by the one to its left. + + As well as field names, one may use literals with Go syntax. + Integer, floating-point, and string literals are supported. + Raw strings may not span newlines. + + The delimiter strings get their default value, "{" and "}", from + JSON-template. They may be set to any non-empty, space-free + string using the SetDelims method. Their value can be printed + in the output using {.meta-left} and {.meta-right}. +*/ +package template diff --git a/src/pkg/template/execute.go b/src/pkg/template/execute.go new file mode 100644 index 000000000..5bc7ff7e9 --- /dev/null +++ b/src/pkg/template/execute.go @@ -0,0 +1,346 @@ +// Copyright 2009 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. + +// Code to execute a parsed template. + +package template + +import ( + "bytes" + "io" + "reflect" + "strings" +) + +// Internal state for executing a Template. As we evaluate the struct, +// the data item descends into the fields associated with sections, etc. +// Parent is used to walk upwards to find variables higher in the tree. +type state struct { + parent *state // parent in hierarchy + data reflect.Value // the driver data for this section etc. + wr io.Writer // where to send output + buf [2]bytes.Buffer // alternating buffers used when chaining formatters +} + +func (parent *state) clone(data reflect.Value) *state { + return &state{parent: parent, data: data, wr: parent.wr} +} + +// Evaluate interfaces and pointers looking for a value that can look up the name, via a +// struct field, method, or map key, and return the result of the lookup. +func (t *Template) lookup(st *state, v reflect.Value, name string) reflect.Value { + for v.IsValid() { + typ := v.Type() + if n := v.Type().NumMethod(); n > 0 { + for i := 0; i < n; i++ { + m := typ.Method(i) + mtyp := m.Type + if m.Name == name && mtyp.NumIn() == 1 && mtyp.NumOut() == 1 { + if !isExported(name) { + t.execError(st, t.linenum, "name not exported: %s in type %s", name, st.data.Type()) + } + return v.Method(i).Call(nil)[0] + } + } + } + switch av := v; av.Kind() { + case reflect.Ptr: + v = av.Elem() + case reflect.Interface: + v = av.Elem() + case reflect.Struct: + if !isExported(name) { + t.execError(st, t.linenum, "name not exported: %s in type %s", name, st.data.Type()) + } + return av.FieldByName(name) + case reflect.Map: + if v := av.MapIndex(reflect.ValueOf(name)); v.IsValid() { + return v + } + return reflect.Zero(typ.Elem()) + default: + return reflect.Value{} + } + } + return v +} + +// indirectPtr returns the item numLevels levels of indirection below the value. +// It is forgiving: if the value is not a pointer, it returns it rather than giving +// an error. If the pointer is nil, it is returned as is. +func indirectPtr(v reflect.Value, numLevels int) reflect.Value { + for i := numLevels; v.IsValid() && i > 0; i++ { + if p := v; p.Kind() == reflect.Ptr { + if p.IsNil() { + return v + } + v = p.Elem() + } else { + break + } + } + return v +} + +// Walk v through pointers and interfaces, extracting the elements within. +func indirect(v reflect.Value) reflect.Value { +loop: + for v.IsValid() { + switch av := v; av.Kind() { + case reflect.Ptr: + v = av.Elem() + case reflect.Interface: + v = av.Elem() + default: + break loop + } + } + return v +} + +// If the data for this template is a struct, find the named variable. +// Names of the form a.b.c are walked down the data tree. +// The special name "@" (the "cursor") denotes the current data. +// The value coming in (st.data) might need indirecting to reach +// a struct while the return value is not indirected - that is, +// it represents the actual named field. Leading stars indicate +// levels of indirection to be applied to the value. +func (t *Template) findVar(st *state, s string) reflect.Value { + data := st.data + flattenedName := strings.TrimLeft(s, "*") + numStars := len(s) - len(flattenedName) + s = flattenedName + if s == "@" { + return indirectPtr(data, numStars) + } + for _, elem := range strings.Split(s, ".", -1) { + // Look up field; data must be a struct or map. + data = t.lookup(st, data, elem) + if !data.IsValid() { + return reflect.Value{} + } + } + return indirectPtr(data, numStars) +} + +// Is there no data to look at? +func empty(v reflect.Value) bool { + v = indirect(v) + if !v.IsValid() { + return true + } + switch v.Kind() { + case reflect.Bool: + return v.Bool() == false + case reflect.String: + return v.String() == "" + case reflect.Struct: + return false + case reflect.Map: + return false + case reflect.Array: + return v.Len() == 0 + case reflect.Slice: + return v.Len() == 0 + } + return false +} + +// Look up a variable or method, up through the parent if necessary. +func (t *Template) varValue(name string, st *state) reflect.Value { + field := t.findVar(st, name) + if !field.IsValid() { + if st.parent == nil { + t.execError(st, t.linenum, "name not found: %s in type %s", name, st.data.Type()) + } + return t.varValue(name, st.parent) + } + return field +} + +func (t *Template) format(wr io.Writer, fmt string, val []interface{}, v *variableElement, st *state) { + fn := t.formatter(fmt) + if fn == nil { + t.execError(st, v.linenum, "missing formatter %s for variable", fmt) + } + fn(wr, fmt, val...) +} + +// Evaluate a variable, looking up through the parent if necessary. +// If it has a formatter attached ({var|formatter}) run that too. +func (t *Template) writeVariable(v *variableElement, st *state) { + // Resolve field names + val := make([]interface{}, len(v.args)) + for i, arg := range v.args { + if name, ok := arg.(fieldName); ok { + val[i] = t.varValue(string(name), st).Interface() + } else { + val[i] = arg + } + } + for i, fmt := range v.fmts[:len(v.fmts)-1] { + b := &st.buf[i&1] + b.Reset() + t.format(b, fmt, val, v, st) + val = val[0:1] + val[0] = b.Bytes() + } + t.format(st.wr, v.fmts[len(v.fmts)-1], val, v, st) +} + +// Execute element i. Return next index to execute. +func (t *Template) executeElement(i int, st *state) int { + switch elem := t.elems[i].(type) { + case *textElement: + st.wr.Write(elem.text) + return i + 1 + case *literalElement: + st.wr.Write(elem.text) + return i + 1 + case *variableElement: + t.writeVariable(elem, st) + return i + 1 + case *sectionElement: + t.executeSection(elem, st) + return elem.end + case *repeatedElement: + t.executeRepeated(elem, st) + return elem.end + } + e := t.elems[i] + t.execError(st, 0, "internal error: bad directive in execute: %v %T\n", reflect.ValueOf(e).Interface(), e) + return 0 +} + +// Execute the template. +func (t *Template) execute(start, end int, st *state) { + for i := start; i < end; { + i = t.executeElement(i, st) + } +} + +// Execute a .section +func (t *Template) executeSection(s *sectionElement, st *state) { + // Find driver data for this section. It must be in the current struct. + field := t.varValue(s.field, st) + if !field.IsValid() { + t.execError(st, s.linenum, ".section: cannot find field %s in %s", s.field, st.data.Type()) + } + st = st.clone(field) + start, end := s.start, s.or + if !empty(field) { + // Execute the normal block. + if end < 0 { + end = s.end + } + } else { + // Execute the .or block. If it's missing, do nothing. + start, end = s.or, s.end + if start < 0 { + return + } + } + for i := start; i < end; { + i = t.executeElement(i, st) + } +} + +// Return the result of calling the Iter method on v, or nil. +func iter(v reflect.Value) reflect.Value { + for j := 0; j < v.Type().NumMethod(); j++ { + mth := v.Type().Method(j) + fv := v.Method(j) + ft := fv.Type() + // TODO(rsc): NumIn() should return 0 here, because ft is from a curried FuncValue. + if mth.Name != "Iter" || ft.NumIn() != 1 || ft.NumOut() != 1 { + continue + } + ct := ft.Out(0) + if ct.Kind() != reflect.Chan || + ct.ChanDir()&reflect.RecvDir == 0 { + continue + } + return fv.Call(nil)[0] + } + return reflect.Value{} +} + +// Execute a .repeated section +func (t *Template) executeRepeated(r *repeatedElement, st *state) { + // Find driver data for this section. It must be in the current struct. + field := t.varValue(r.field, st) + if !field.IsValid() { + t.execError(st, r.linenum, ".repeated: cannot find field %s in %s", r.field, st.data.Type()) + } + field = indirect(field) + + start, end := r.start, r.or + if end < 0 { + end = r.end + } + if r.altstart >= 0 { + end = r.altstart + } + first := true + + // Code common to all the loops. + loopBody := func(newst *state) { + // .alternates between elements + if !first && r.altstart >= 0 { + for i := r.altstart; i < r.altend; { + i = t.executeElement(i, newst) + } + } + first = false + for i := start; i < end; { + i = t.executeElement(i, newst) + } + } + + if array := field; array.Kind() == reflect.Array || array.Kind() == reflect.Slice { + for j := 0; j < array.Len(); j++ { + loopBody(st.clone(array.Index(j))) + } + } else if m := field; m.Kind() == reflect.Map { + for _, key := range m.MapKeys() { + loopBody(st.clone(m.MapIndex(key))) + } + } else if ch := iter(field); ch.IsValid() { + for { + e, ok := ch.Recv() + if !ok { + break + } + loopBody(st.clone(e)) + } + } else { + t.execError(st, r.linenum, ".repeated: cannot repeat %s (type %s)", + r.field, field.Type()) + } + + if first { + // Empty. Execute the .or block, once. If it's missing, do nothing. + start, end := r.or, r.end + if start >= 0 { + newst := st.clone(field) + for i := start; i < end; { + i = t.executeElement(i, newst) + } + } + return + } +} + +// A valid delimiter must contain no space and be non-empty. +func validDelim(d []byte) bool { + if len(d) == 0 { + return false + } + for _, c := range d { + if isSpace(c) { + return false + } + } + return true +} diff --git a/src/pkg/template/template.go b/src/pkg/template/parse.go index f481cbd1e..b4aa5fcd2 100644 --- a/src/pkg/template/template.go +++ b/src/pkg/template/parse.go @@ -2,97 +2,11 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -/* - Package template implements data-driven templates for generating textual - output such as HTML. +// Code to parse a template. - Templates are executed by applying them to a data structure. - Annotations in the template refer to elements of the data - structure (typically a field of a struct or a key in a map) - to control execution and derive values to be displayed. - The template walks the structure as it executes and the - "cursor" @ represents the value at the current location - in the structure. - - Data items may be values or pointers; the interface hides the - indirection. - - In the following, 'Field' is one of several things, according to the data. - - - The name of a field of a struct (result = data.Field), - - The value stored in a map under that key (result = data["Field"]), or - - The result of invoking a niladic single-valued method with that name - (result = data.Field()) - - If Field is a struct field or method name, it must be an exported - (capitalized) name. - - Major constructs ({} are the default delimiters for template actions; - [] are the notation in this comment for optional elements): - - {# comment } - - A one-line comment. - - {.section field} XXX [ {.or} YYY ] {.end} - - Set @ to the value of the field. It may be an explicit @ - to stay at the same point in the data. If the field is nil - or empty, execute YYY; otherwise execute XXX. - - {.repeated section field} XXX [ {.alternates with} ZZZ ] [ {.or} YYY ] {.end} - - Like .section, but field must be an array or slice. XXX - is executed for each element. If the array is nil or empty, - YYY is executed instead. If the {.alternates with} marker - is present, ZZZ is executed between iterations of XXX. - - {field} - {field1 field2 ...} - {field|formatter} - {field1 field2...|formatter} - {field|formatter1|formatter2} - - Insert the value of the fields into the output. Each field is - first looked for in the cursor, as in .section and .repeated. - If it is not found, the search continues in outer sections - until the top level is reached. - - If the field value is a pointer, leading asterisks indicate - that the value to be inserted should be evaluated through the - pointer. For example, if x.p is of type *int, {x.p} will - insert the value of the pointer but {*x.p} will insert the - value of the underlying integer. If the value is nil or not a - pointer, asterisks have no effect. - - If a formatter is specified, it must be named in the formatter - map passed to the template set up routines or in the default - set ("html","str","") and is used to process the data for - output. The formatter function has signature - func(wr io.Writer, formatter string, data ...interface{}) - where wr is the destination for output, data holds the field - values at the instantiation, and formatter is its name at - the invocation site. The default formatter just concatenates - the string representations of the fields. - - Multiple formatters separated by the pipeline character | are - executed sequentially, with each formatter receiving the bytes - emitted by the one to its left. - - As well as field names, one may use literals with Go syntax. - Integer, floating-point, and string literals are supported. - Raw strings may not span newlines. - - The delimiter strings get their default value, "{" and "}", from - JSON-template. They may be set to any non-empty, space-free - string using the SetDelims method. Their value can be printed - in the output using {.meta-left} and {.meta-right}. -*/ package template import ( - "bytes" - "container/vector" "fmt" "io" "io/ioutil" @@ -113,6 +27,19 @@ type Error struct { func (e *Error) String() string { return fmt.Sprintf("line %d: %s", e.Line, e.Msg) } +// checkError is a deferred function to turn a panic with type *Error into a plain error return. +// Other panics are unexpected and so are re-enabled. +func checkError(error *os.Error) { + if v := recover(); v != nil { + if e, ok := v.(*Error); ok { + *error = e + } else { + // runtime errors should crash + panic(v) + } + } +} + // Most of the literals are aces. var lbrace = []byte{'{'} var rbrace = []byte{'}'} @@ -192,21 +119,7 @@ type Template struct { p int // position in buf linenum int // position in input // Parsed results: - elems *vector.Vector -} - -// Internal state for executing a Template. As we evaluate the struct, -// the data item descends into the fields associated with sections, etc. -// Parent is used to walk upwards to find variables higher in the tree. -type state struct { - parent *state // parent in hierarchy - data reflect.Value // the driver data for this section etc. - wr io.Writer // where to send output - buf [2]bytes.Buffer // alternating buffers used when chaining formatters -} - -func (parent *state) clone(data reflect.Value) *state { - return &state{parent: parent, data: data, wr: parent.wr} + elems []interface{} } // New creates a new template with the specified formatter map (which @@ -216,7 +129,7 @@ func New(fmap FormatterMap) *Template { t.fmap = fmap t.ldelim = lbrace t.rdelim = rbrace - t.elems = new(vector.Vector) + t.elems = make([]interface{}, 0, 16) return t } @@ -583,24 +496,24 @@ func (t *Template) parseSimple(item []byte) (done bool, tok int, w []string) { case tokComment: return case tokText: - t.elems.Push(&textElement{item}) + t.elems = append(t.elems, &textElement{item}) return case tokLiteral: switch w[0] { case ".meta-left": - t.elems.Push(&literalElement{t.ldelim}) + t.elems = append(t.elems, &literalElement{t.ldelim}) case ".meta-right": - t.elems.Push(&literalElement{t.rdelim}) + t.elems = append(t.elems, &literalElement{t.rdelim}) case ".space": - t.elems.Push(&literalElement{space}) + t.elems = append(t.elems, &literalElement{space}) case ".tab": - t.elems.Push(&literalElement{tab}) + t.elems = append(t.elems, &literalElement{tab}) default: t.parseError("internal error: unknown literal: %s", w[0]) } return case tokVariable: - t.elems.Push(t.newVariable(w)) + t.elems = append(t.elems, t.newVariable(w)) return } return false, tok, w @@ -610,11 +523,11 @@ func (t *Template) parseSimple(item []byte) (done bool, tok int, w []string) { func (t *Template) parseRepeated(words []string) *repeatedElement { r := new(repeatedElement) - t.elems.Push(r) + t.elems = append(t.elems, r) r.linenum = t.linenum r.field = words[2] // Scan section, collecting true and false (.or) blocks. - r.start = t.elems.Len() + r.start = len(t.elems) r.or = -1 r.altstart = -1 r.altend = -1 @@ -637,8 +550,8 @@ Loop: t.parseError("extra .or in .repeated section") break Loop } - r.altend = t.elems.Len() - r.or = t.elems.Len() + r.altend = len(t.elems) + r.or = len(t.elems) case tokSection: t.parseSection(w) case tokRepeated: @@ -652,26 +565,26 @@ Loop: t.parseError(".alternates inside .or block in .repeated section") break Loop } - r.altstart = t.elems.Len() + r.altstart = len(t.elems) default: t.parseError("internal error: unknown repeated section item: %s", item) break Loop } } if r.altend < 0 { - r.altend = t.elems.Len() + r.altend = len(t.elems) } - r.end = t.elems.Len() + r.end = len(t.elems) return r } func (t *Template) parseSection(words []string) *sectionElement { s := new(sectionElement) - t.elems.Push(s) + t.elems = append(t.elems, s) s.linenum = t.linenum s.field = words[1] // Scan section, collecting true and false (.or) blocks. - s.start = t.elems.Len() + s.start = len(t.elems) s.or = -1 Loop: for { @@ -692,7 +605,7 @@ Loop: t.parseError("extra .or in .section") break Loop } - s.or = t.elems.Len() + s.or = len(t.elems) case tokSection: t.parseSection(w) case tokRepeated: @@ -703,7 +616,7 @@ Loop: t.parseError("internal error: unknown section item: %s", item) } } - s.end = t.elems.Len() + s.end = len(t.elems) return s } @@ -732,337 +645,6 @@ func (t *Template) parse() { // -- Execution -// Evaluate interfaces and pointers looking for a value that can look up the name, via a -// struct field, method, or map key, and return the result of the lookup. -func (t *Template) lookup(st *state, v reflect.Value, name string) reflect.Value { - for v.IsValid() { - typ := v.Type() - if n := v.Type().NumMethod(); n > 0 { - for i := 0; i < n; i++ { - m := typ.Method(i) - mtyp := m.Type - if m.Name == name && mtyp.NumIn() == 1 && mtyp.NumOut() == 1 { - if !isExported(name) { - t.execError(st, t.linenum, "name not exported: %s in type %s", name, st.data.Type()) - } - return v.Method(i).Call(nil)[0] - } - } - } - switch av := v; av.Kind() { - case reflect.Ptr: - v = av.Elem() - case reflect.Interface: - v = av.Elem() - case reflect.Struct: - if !isExported(name) { - t.execError(st, t.linenum, "name not exported: %s in type %s", name, st.data.Type()) - } - return av.FieldByName(name) - case reflect.Map: - if v := av.MapIndex(reflect.ValueOf(name)); v.IsValid() { - return v - } - return reflect.Zero(typ.Elem()) - default: - return reflect.Value{} - } - } - return v -} - -// indirectPtr returns the item numLevels levels of indirection below the value. -// It is forgiving: if the value is not a pointer, it returns it rather than giving -// an error. If the pointer is nil, it is returned as is. -func indirectPtr(v reflect.Value, numLevels int) reflect.Value { - for i := numLevels; v.IsValid() && i > 0; i++ { - if p := v; p.Kind() == reflect.Ptr { - if p.IsNil() { - return v - } - v = p.Elem() - } else { - break - } - } - return v -} - -// Walk v through pointers and interfaces, extracting the elements within. -func indirect(v reflect.Value) reflect.Value { -loop: - for v.IsValid() { - switch av := v; av.Kind() { - case reflect.Ptr: - v = av.Elem() - case reflect.Interface: - v = av.Elem() - default: - break loop - } - } - return v -} - -// If the data for this template is a struct, find the named variable. -// Names of the form a.b.c are walked down the data tree. -// The special name "@" (the "cursor") denotes the current data. -// The value coming in (st.data) might need indirecting to reach -// a struct while the return value is not indirected - that is, -// it represents the actual named field. Leading stars indicate -// levels of indirection to be applied to the value. -func (t *Template) findVar(st *state, s string) reflect.Value { - data := st.data - flattenedName := strings.TrimLeft(s, "*") - numStars := len(s) - len(flattenedName) - s = flattenedName - if s == "@" { - return indirectPtr(data, numStars) - } - for _, elem := range strings.Split(s, ".", -1) { - // Look up field; data must be a struct or map. - data = t.lookup(st, data, elem) - if !data.IsValid() { - return reflect.Value{} - } - } - return indirectPtr(data, numStars) -} - -// Is there no data to look at? -func empty(v reflect.Value) bool { - v = indirect(v) - if !v.IsValid() { - return true - } - switch v.Kind() { - case reflect.Bool: - return v.Bool() == false - case reflect.String: - return v.String() == "" - case reflect.Struct: - return false - case reflect.Map: - return false - case reflect.Array: - return v.Len() == 0 - case reflect.Slice: - return v.Len() == 0 - } - return false -} - -// Look up a variable or method, up through the parent if necessary. -func (t *Template) varValue(name string, st *state) reflect.Value { - field := t.findVar(st, name) - if !field.IsValid() { - if st.parent == nil { - t.execError(st, t.linenum, "name not found: %s in type %s", name, st.data.Type()) - } - return t.varValue(name, st.parent) - } - return field -} - -func (t *Template) format(wr io.Writer, fmt string, val []interface{}, v *variableElement, st *state) { - fn := t.formatter(fmt) - if fn == nil { - t.execError(st, v.linenum, "missing formatter %s for variable", fmt) - } - fn(wr, fmt, val...) -} - -// Evaluate a variable, looking up through the parent if necessary. -// If it has a formatter attached ({var|formatter}) run that too. -func (t *Template) writeVariable(v *variableElement, st *state) { - // Resolve field names - val := make([]interface{}, len(v.args)) - for i, arg := range v.args { - if name, ok := arg.(fieldName); ok { - val[i] = t.varValue(string(name), st).Interface() - } else { - val[i] = arg - } - } - for i, fmt := range v.fmts[:len(v.fmts)-1] { - b := &st.buf[i&1] - b.Reset() - t.format(b, fmt, val, v, st) - val = val[0:1] - val[0] = b.Bytes() - } - t.format(st.wr, v.fmts[len(v.fmts)-1], val, v, st) -} - -// Execute element i. Return next index to execute. -func (t *Template) executeElement(i int, st *state) int { - switch elem := t.elems.At(i).(type) { - case *textElement: - st.wr.Write(elem.text) - return i + 1 - case *literalElement: - st.wr.Write(elem.text) - return i + 1 - case *variableElement: - t.writeVariable(elem, st) - return i + 1 - case *sectionElement: - t.executeSection(elem, st) - return elem.end - case *repeatedElement: - t.executeRepeated(elem, st) - return elem.end - } - e := t.elems.At(i) - t.execError(st, 0, "internal error: bad directive in execute: %v %T\n", reflect.ValueOf(e).Interface(), e) - return 0 -} - -// Execute the template. -func (t *Template) execute(start, end int, st *state) { - for i := start; i < end; { - i = t.executeElement(i, st) - } -} - -// Execute a .section -func (t *Template) executeSection(s *sectionElement, st *state) { - // Find driver data for this section. It must be in the current struct. - field := t.varValue(s.field, st) - if !field.IsValid() { - t.execError(st, s.linenum, ".section: cannot find field %s in %s", s.field, st.data.Type()) - } - st = st.clone(field) - start, end := s.start, s.or - if !empty(field) { - // Execute the normal block. - if end < 0 { - end = s.end - } - } else { - // Execute the .or block. If it's missing, do nothing. - start, end = s.or, s.end - if start < 0 { - return - } - } - for i := start; i < end; { - i = t.executeElement(i, st) - } -} - -// Return the result of calling the Iter method on v, or nil. -func iter(v reflect.Value) reflect.Value { - for j := 0; j < v.Type().NumMethod(); j++ { - mth := v.Type().Method(j) - fv := v.Method(j) - ft := fv.Type() - // TODO(rsc): NumIn() should return 0 here, because ft is from a curried FuncValue. - if mth.Name != "Iter" || ft.NumIn() != 1 || ft.NumOut() != 1 { - continue - } - ct := ft.Out(0) - if ct.Kind() != reflect.Chan || - ct.ChanDir()&reflect.RecvDir == 0 { - continue - } - return fv.Call(nil)[0] - } - return reflect.Value{} -} - -// Execute a .repeated section -func (t *Template) executeRepeated(r *repeatedElement, st *state) { - // Find driver data for this section. It must be in the current struct. - field := t.varValue(r.field, st) - if !field.IsValid() { - t.execError(st, r.linenum, ".repeated: cannot find field %s in %s", r.field, st.data.Type()) - } - field = indirect(field) - - start, end := r.start, r.or - if end < 0 { - end = r.end - } - if r.altstart >= 0 { - end = r.altstart - } - first := true - - // Code common to all the loops. - loopBody := func(newst *state) { - // .alternates between elements - if !first && r.altstart >= 0 { - for i := r.altstart; i < r.altend; { - i = t.executeElement(i, newst) - } - } - first = false - for i := start; i < end; { - i = t.executeElement(i, newst) - } - } - - if array := field; array.Kind() == reflect.Array || array.Kind() == reflect.Slice { - for j := 0; j < array.Len(); j++ { - loopBody(st.clone(array.Index(j))) - } - } else if m := field; m.Kind() == reflect.Map { - for _, key := range m.MapKeys() { - loopBody(st.clone(m.MapIndex(key))) - } - } else if ch := iter(field); ch.IsValid() { - for { - e, ok := ch.Recv() - if !ok { - break - } - loopBody(st.clone(e)) - } - } else { - t.execError(st, r.linenum, ".repeated: cannot repeat %s (type %s)", - r.field, field.Type()) - } - - if first { - // Empty. Execute the .or block, once. If it's missing, do nothing. - start, end := r.or, r.end - if start >= 0 { - newst := st.clone(field) - for i := start; i < end; { - i = t.executeElement(i, newst) - } - } - return - } -} - -// A valid delimiter must contain no space and be non-empty. -func validDelim(d []byte) bool { - if len(d) == 0 { - return false - } - for _, c := range d { - if isSpace(c) { - return false - } - } - return true -} - -// checkError is a deferred function to turn a panic with type *Error into a plain error return. -// Other panics are unexpected and so are re-enabled. -func checkError(error *os.Error) { - if v := recover(); v != nil { - if e, ok := v.(*Error); ok { - *error = e - } else { - // runtime errors should crash - panic(v) - } - } -} - // -- Public interface // Parse initializes a Template by parsing its definition. The string @@ -1100,7 +682,7 @@ func (t *Template) Execute(wr io.Writer, data interface{}) (err os.Error) { val := reflect.ValueOf(data) defer checkError(&err) t.p = 0 - t.execute(0, t.elems.Len(), &state{parent: nil, data: val, wr: wr}) + t.execute(0, len(t.elems), &state{parent: nil, data: val, wr: wr}) return nil } diff --git a/src/pkg/testing/benchmark.go b/src/pkg/testing/benchmark.go index db4c65941..f8b53e63a 100644 --- a/src/pkg/testing/benchmark.go +++ b/src/pkg/testing/benchmark.go @@ -143,7 +143,6 @@ func (b *B) run() BenchmarkResult { b.runN(n) } return BenchmarkResult{b.N, b.ns, b.bytes} - } // The results of a benchmark run. @@ -183,6 +182,7 @@ func RunBenchmarks(matchString func(pat, str string) (bool, os.Error), benchmark if len(*matchBenchmarks) == 0 { return } + procs := runtime.GOMAXPROCS(-1) for _, Benchmark := range benchmarks { matched, err := matchString(*matchBenchmarks, Benchmark.Name) if err != nil { @@ -194,7 +194,12 @@ func RunBenchmarks(matchString func(pat, str string) (bool, os.Error), benchmark } b := &B{benchmark: Benchmark} r := b.run() - fmt.Printf("%s\t%v\n", Benchmark.Name, r) + print(fmt.Sprintf("%s\t%v\n", Benchmark.Name, r)) + if p := runtime.GOMAXPROCS(-1); p != procs { + print(fmt.Sprintf("%s left GOMAXPROCS set to %d\n", Benchmark.Name, p)) + procs = p + } + } } diff --git a/src/pkg/testing/testing.go b/src/pkg/testing/testing.go index 8781b207d..3b2dd377a 100644 --- a/src/pkg/testing/testing.go +++ b/src/pkg/testing/testing.go @@ -171,6 +171,7 @@ func RunTests(matchString func(pat, str string) (bool, os.Error), tests []Intern if len(tests) == 0 { println("testing: warning: no tests to run") } + procs := runtime.GOMAXPROCS(-1) for i := 0; i < len(tests); i++ { matched, err := matchString(*match, tests[i].Name) if err != nil { @@ -190,6 +191,11 @@ func RunTests(matchString func(pat, str string) (bool, os.Error), tests []Intern <-t.ch ns += time.Nanoseconds() tstr := fmt.Sprintf("(%.2f seconds)", float64(ns)/1e9) + if p := runtime.GOMAXPROCS(-1); t.failed == false && p != procs { + t.failed = true + t.errors = fmt.Sprintf("%s left GOMAXPROCS set to %d\n", tests[i].Name, p) + procs = p + } if t.failed { println("--- FAIL:", tests[i].Name, tstr) print(t.errors) diff --git a/src/pkg/unicode/Makefile b/src/pkg/unicode/Makefile index 26e6e501f..55ed5b2d9 100644 --- a/src/pkg/unicode/Makefile +++ b/src/pkg/unicode/Makefile @@ -8,6 +8,7 @@ TARG=unicode GOFILES=\ casetables.go\ digit.go\ + graphic.go\ letter.go\ tables.go\ diff --git a/src/pkg/unicode/digit.go b/src/pkg/unicode/digit.go index 471c4dfdc..6793fd7e5 100644 --- a/src/pkg/unicode/digit.go +++ b/src/pkg/unicode/digit.go @@ -6,7 +6,7 @@ package unicode // IsDigit reports whether the rune is a decimal digit. func IsDigit(rune int) bool { - if rune < 0x100 { // quick ASCII (Latin-1, really) check + if rune <= MaxLatin1 { return '0' <= rune && rune <= '9' } return Is(Digit, rune) diff --git a/src/pkg/unicode/digit_test.go b/src/pkg/unicode/digit_test.go index 9bbccde92..ae3c0ece9 100644 --- a/src/pkg/unicode/digit_test.go +++ b/src/pkg/unicode/digit_test.go @@ -118,7 +118,7 @@ func TestDigit(t *testing.T) { // Test that the special case in IsDigit agrees with the table func TestDigitOptimization(t *testing.T) { - for i := 0; i < 0x100; i++ { + for i := 0; i <= MaxLatin1; i++ { if Is(Digit, i) != IsDigit(i) { t.Errorf("IsDigit(U+%04X) disagrees with Is(Digit)", i) } diff --git a/src/pkg/unicode/graphic.go b/src/pkg/unicode/graphic.go new file mode 100644 index 000000000..d482aace2 --- /dev/null +++ b/src/pkg/unicode/graphic.go @@ -0,0 +1,132 @@ +// Copyright 2011 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 unicode + +// Bit masks for each code point under U+0100, for fast lookup. +const ( + pC = 1 << iota // a control character. + pP // a punctuation character. + pN // a numeral. + pS // a symbolic character. + pZ // a spacing character. + pLu // an upper-case letter. + pLl // a lower-case letter. + pp // a printable character according to Go's definition. + pg = pp | pZ // a graphical character according to the Unicode definition. +) + +// GraphicRanges defines the set of graphic characters according to Unicode. +var GraphicRanges = []*RangeTable{ + L, M, N, P, S, Zs, +} + +// PrintRanges defines the set of printable characters according to Go. +// ASCII space, U+0020, is handled separately. +var PrintRanges = []*RangeTable{ + L, M, N, P, S, +} + +// IsGraphic reports whether the rune is defined as a Graphic by Unicode. +// Such characters include letters, marks, numbers, punctuation, symbols, and +// spaces, from categories L, M, N, P, S, Zs. +func IsGraphic(rune int) bool { + // We cast to uint32 to avoid the extra test for negative, + // and in the index we cast to uint8 to avoid the range check. + if uint32(rune) <= MaxLatin1 { + return properties[uint8(rune)]&pg != 0 + } + return IsOneOf(GraphicRanges, rune) +} + +// IsPrint reports whether the rune is defined as printable by Go. Such +// characters include letters, marks, numbers, punctuation, symbols, and the +// ASCII space character, from categories L, M, N, P, S and the ASCII space +// character. This categorization is the same as IsGraphic except that the +// only spacing character is ASCII space, U+0020. +func IsPrint(rune int) bool { + if uint32(rune) <= MaxLatin1 { + return properties[uint8(rune)]&pp != 0 + } + return IsOneOf(PrintRanges, rune) +} + +// IsOneOf reports whether the rune is a member of one of the ranges. +// The rune is known to be above Latin-1. +func IsOneOf(set []*RangeTable, rune int) bool { + for _, inside := range set { + if Is(inside, rune) { + return true + } + } + return false +} + +// IsControl reports whether the rune is a control character. +// The C (Other) Unicode category includes more code points +// such as surrogates; use Is(C, rune) to test for them. +func IsControl(rune int) bool { + if uint32(rune) <= MaxLatin1 { + return properties[uint8(rune)]&pC != 0 + } + // All control characters are < Latin1Max. + return false +} + +// IsLetter reports whether the rune is a letter (category L). +func IsLetter(rune int) bool { + if uint32(rune) <= MaxLatin1 { + return properties[uint8(rune)]&(pLu|pLl) != 0 + } + return Is(Letter, rune) +} + +// IsMark reports whether the rune is a mark character (category M). +func IsMark(rune int) bool { + // There are no mark characters in Latin-1. + return Is(Mark, rune) +} + +// IsNumber reports whether the rune is a number (category N). +func IsNumber(rune int) bool { + if uint32(rune) <= MaxLatin1 { + return properties[uint8(rune)]&pN != 0 + } + return Is(Number, rune) +} + +// IsPunct reports whether the rune is a Unicode punctuation character +// (category P). +func IsPunct(rune int) bool { + if uint32(rune) <= MaxLatin1 { + return properties[uint8(rune)]&pP != 0 + } + return Is(Punct, rune) +} + +// IsSpace reports whether the rune is a space character as defined +// by Unicode's White Space property; in the Latin-1 space +// this is +// '\t', '\n', '\v', '\f', '\r', ' ', U+0085 (NEL), U+00A0 (NBSP). +// Other definitions of spacing characters are set by category +// Z and property Pattern_White_Space. +func IsSpace(rune int) bool { + // This property isn't the same as Z; special-case it. + if uint32(rune) <= MaxLatin1 { + switch rune { + case '\t', '\n', '\v', '\f', '\r', ' ', 0x85, 0xA0: + return true + } + return false + } + return Is(White_Space, rune) +} + +// IsSymbol reports whether the rune is a symbolic character. +func IsSymbol(rune int) bool { + if uint32(rune) <= MaxLatin1 { + return properties[uint8(rune)]&pS != 0 + } + return Is(Symbol, rune) +} diff --git a/src/pkg/unicode/graphic_test.go b/src/pkg/unicode/graphic_test.go new file mode 100644 index 000000000..77c679f7c --- /dev/null +++ b/src/pkg/unicode/graphic_test.go @@ -0,0 +1,122 @@ +// Copyright 2011 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 unicode_test + +import ( + "testing" + . "unicode" +) + +// Independently check that the special "Is" functions work +// in the Latin-1 range through the property table. + +func TestIsControlLatin1(t *testing.T) { + for i := 0; i <= MaxLatin1; i++ { + got := IsControl(i) + want := false + switch { + case 0x00 <= i && i <= 0x1F: + want = true + case 0x7F <= i && i <= 0x9F: + want = true + } + if got != want { + t.Errorf("%U incorrect: got %t; want %t", i, got, want) + } + } +} + +func TestIsLetterLatin1(t *testing.T) { + for i := 0; i <= MaxLatin1; i++ { + got := IsLetter(i) + want := Is(Letter, i) + if got != want { + t.Errorf("%U incorrect: got %t; want %t", i, got, want) + } + } +} + +func TestIsUpperLatin1(t *testing.T) { + for i := 0; i <= MaxLatin1; i++ { + got := IsUpper(i) + want := Is(Upper, i) + if got != want { + t.Errorf("%U incorrect: got %t; want %t", i, got, want) + } + } +} + +func TestIsLowerLatin1(t *testing.T) { + for i := 0; i <= MaxLatin1; i++ { + got := IsLower(i) + want := Is(Lower, i) + if got != want { + t.Errorf("%U incorrect: got %t; want %t", i, got, want) + } + } +} + +func TestNumberLatin1(t *testing.T) { + for i := 0; i <= MaxLatin1; i++ { + got := IsNumber(i) + want := Is(Number, i) + if got != want { + t.Errorf("%U incorrect: got %t; want %t", i, got, want) + } + } +} + +func TestIsPrintLatin1(t *testing.T) { + for i := 0; i <= MaxLatin1; i++ { + got := IsPrint(i) + want := IsOneOf(PrintRanges, i) + if i == ' ' { + want = true + } + if got != want { + t.Errorf("%U incorrect: got %t; want %t", i, got, want) + } + } +} + +func TestIsGraphicLatin1(t *testing.T) { + for i := 0; i <= MaxLatin1; i++ { + got := IsGraphic(i) + want := IsOneOf(GraphicRanges, i) + if got != want { + t.Errorf("%U incorrect: got %t; want %t", i, got, want) + } + } +} + +func TestIsPunctLatin1(t *testing.T) { + for i := 0; i <= MaxLatin1; i++ { + got := IsPunct(i) + want := Is(Punct, i) + if got != want { + t.Errorf("%U incorrect: got %t; want %t", i, got, want) + } + } +} + +func TestIsSpaceLatin1(t *testing.T) { + for i := 0; i <= MaxLatin1; i++ { + got := IsSpace(i) + want := Is(White_Space, i) + if got != want { + t.Errorf("%U incorrect: got %t; want %t", i, got, want) + } + } +} + +func TestIsSymbolLatin1(t *testing.T) { + for i := 0; i <= MaxLatin1; i++ { + got := IsSymbol(i) + want := Is(Symbol, i) + if got != want { + t.Errorf("%U incorrect: got %t; want %t", i, got, want) + } + } +} diff --git a/src/pkg/unicode/letter.go b/src/pkg/unicode/letter.go index 047bef19b..a0c55bbf7 100644 --- a/src/pkg/unicode/letter.go +++ b/src/pkg/unicode/letter.go @@ -9,6 +9,8 @@ package unicode const ( MaxRune = 0x10FFFF // Maximum valid Unicode code point. ReplacementChar = 0xFFFD // Represents invalid code points. + MaxASCII = 0x7F // maximum ASCII value. + MaxLatin1 = 0xFF // maximum Latin-1 value. ) // RangeTable defines a set of Unicode code points by listing the ranges of @@ -121,7 +123,7 @@ func is32(ranges []Range32, rune uint32) bool { // Is tests whether rune is in the specified table of ranges. func Is(rangeTab *RangeTable, rune int) bool { // common case: rune is ASCII or Latin-1. - if rune < 0x100 { + if uint32(rune) <= MaxLatin1 { // Only need to check R16, since R32 is always >= 1<<16. r16 := uint16(rune) for _, r := range rangeTab.R16 { @@ -148,49 +150,30 @@ func Is(rangeTab *RangeTable, rune int) bool { // IsUpper reports whether the rune is an upper case letter. func IsUpper(rune int) bool { - if rune < 0x80 { // quick ASCII check - return 'A' <= rune && rune <= 'Z' + // See comment in IsGraphic. + if uint32(rune) <= MaxLatin1 { + return properties[uint8(rune)]&pLu != 0 } return Is(Upper, rune) } // IsLower reports whether the rune is a lower case letter. func IsLower(rune int) bool { - if rune < 0x80 { // quick ASCII check - return 'a' <= rune && rune <= 'z' + // See comment in IsGraphic. + if uint32(rune) <= MaxLatin1 { + return properties[uint8(rune)]&pLl != 0 } return Is(Lower, rune) } // IsTitle reports whether the rune is a title case letter. func IsTitle(rune int) bool { - if rune < 0x80 { // quick ASCII check + if rune <= MaxLatin1 { return false } return Is(Title, rune) } -// IsLetter reports whether the rune is a letter. -func IsLetter(rune int) bool { - if rune < 0x80 { // quick ASCII check - rune &^= 'a' - 'A' - return 'A' <= rune && rune <= 'Z' - } - return Is(Letter, rune) -} - -// IsSpace reports whether the rune is a white space character. -func IsSpace(rune int) bool { - if rune <= 0xFF { // quick Latin-1 check - switch rune { - case '\t', '\n', '\v', '\f', '\r', ' ', 0x85, 0xA0: - return true - } - return false - } - return Is(White_Space, rune) -} - // to maps the rune using the specified case mapping. func to(_case int, rune int, caseRange []CaseRange) int { if _case < 0 || MaxCase <= _case { @@ -235,7 +218,7 @@ func To(_case int, rune int) int { // ToUpper maps the rune to upper case. func ToUpper(rune int) int { - if rune < 0x80 { // quick ASCII check + if rune <= MaxASCII { if 'a' <= rune && rune <= 'z' { rune -= 'a' - 'A' } @@ -246,7 +229,7 @@ func ToUpper(rune int) int { // ToLower maps the rune to lower case. func ToLower(rune int) int { - if rune < 0x80 { // quick ASCII check + if rune <= MaxASCII { if 'A' <= rune && rune <= 'Z' { rune += 'a' - 'A' } @@ -257,7 +240,7 @@ func ToLower(rune int) int { // ToTitle maps the rune to title case. func ToTitle(rune int) int { - if rune < 0x80 { // quick ASCII check + if rune <= MaxASCII { if 'a' <= rune && rune <= 'z' { // title case is upper case for ASCII rune -= 'a' - 'A' } diff --git a/src/pkg/unicode/letter_test.go b/src/pkg/unicode/letter_test.go index 432ffb671..4c24ffc51 100644 --- a/src/pkg/unicode/letter_test.go +++ b/src/pkg/unicode/letter_test.go @@ -323,7 +323,7 @@ func TestIsSpace(t *testing.T) { // Check that the optimizations for IsLetter etc. agree with the tables. // We only need to check the Latin-1 range. func TestLetterOptimizations(t *testing.T) { - for i := 0; i < 0x100; i++ { + for i := 0; i <= MaxLatin1; i++ { if Is(Letter, i) != IsLetter(i) { t.Errorf("IsLetter(U+%04X) disagrees with Is(Letter)", i) } diff --git a/src/pkg/unicode/maketables.go b/src/pkg/unicode/maketables.go index c3cf32b48..655fe46e4 100644 --- a/src/pkg/unicode/maketables.go +++ b/src/pkg/unicode/maketables.go @@ -28,6 +28,7 @@ func main() { printScriptOrProperty(false) printScriptOrProperty(true) printCases() + printLatinProperties() printSizes() } @@ -54,7 +55,17 @@ var test = flag.Bool("test", var scriptRe = regexp.MustCompile(`^([0-9A-F]+)(\.\.[0-9A-F]+)? *; ([A-Za-z_]+)$`) var logger = log.New(os.Stderr, "", log.Lshortfile) -var category = map[string]bool{"letter": true} // Nd Lu etc. letter is a special case +var category = map[string]bool{ + // Nd Lu etc. + // We use one-character names to identify merged categories + "L": true, // Lu Ll Lt Lm Lo + "P": true, // Pc Pd Ps Pe Pu Pf Po + "M": true, // Mn Mc Me + "N": true, // Nd Nl No + "S": true, // Sm Sc Sk So + "Z": true, // Zs Zl Zp + "C": true, // Cc Cf Cs Co Cn +} // UnicodeData.txt has form: // 0037;DIGIT SEVEN;Nd;0;EN;;7;7;7;N;;;;; @@ -247,12 +258,9 @@ func version() string { return "Unknown" } -func letterOp(code int) bool { - switch chars[code].category { - case "Lu", "Ll", "Lt", "Lm", "Lo": - return true - } - return false +func categoryOp(code int, class uint8) bool { + category := chars[code].category + return len(category) > 0 && category[0] == class } func loadChars() { @@ -348,8 +356,27 @@ func printCategories() { // Cases deserving special comments varDecl := "" switch name { - case "letter": - varDecl = "\tLetter = letter; // Letter is the set of Unicode letters.\n" + case "C": + varDecl = "\tOther = _C; // Other/C is the set of Unicode control and special characters, category C.\n" + varDecl += "\tC = _C\n" + case "L": + varDecl = "\tLetter = _L; // Letter/L is the set of Unicode letters, category L.\n" + varDecl += "\tL = _L\n" + case "M": + varDecl = "\tMark = _M; // Mark/M is the set of Unicode mark characters, category M.\n" + varDecl += "\tM = _M\n" + case "N": + varDecl = "\tNumber = _N; // Number/N is the set of Unicode number characters, category N.\n" + varDecl += "\tN = _N\n" + case "P": + varDecl = "\tPunct = _P; // Punct/P is the set of Unicode punctuation characters, category P.\n" + varDecl += "\tP = _P\n" + case "S": + varDecl = "\tSymbol = _S; // Symbol/S is the set of Unicode symbol characters, category S.\n" + varDecl += "\tS = _S\n" + case "Z": + varDecl = "\tSpace = _Z; // Space/Z is the set of Unicode space characters, category Z.\n" + varDecl += "\tZ = _Z\n" case "Nd": varDecl = "\tDigit = _Nd; // Digit is the set of Unicode characters with the \"decimal digit\" property.\n" case "Lu": @@ -359,17 +386,18 @@ func printCategories() { case "Lt": varDecl = "\tTitle = _Lt; // Title is the set of Unicode title case letters.\n" } - if name != "letter" { + if len(name) > 1 { varDecl += fmt.Sprintf( "\t%s = _%s; // %s is the set of Unicode characters in category %s.\n", name, name, name, name) } decl[ndecl] = varDecl ndecl++ - if name == "letter" { // special case + if len(name) == 1 { // unified categories + decl := fmt.Sprintf("var _%s = &RangeTable{\n", name) dumpRange( - "var letter = &RangeTable{\n", - letterOp) + decl, + func(code int) bool { return categoryOp(code, name[0]) }) continue } dumpRange( @@ -446,7 +474,7 @@ func printRange(lo, hi, stride uint32, size int, count *int) (int, *int) { if size == 16 && hi >= 1<<16 { if lo < 1<<16 { if lo+stride != hi { - log.Fatalf("unexpected straddle: %U %U %d", lo, hi, stride) + logger.Fatalf("unexpected straddle: %U %U %d", lo, hi, stride) } // No range contains U+FFFF as an instance, so split // the range into two entries. That way we can maintain @@ -472,11 +500,11 @@ func fullCategoryTest(list []string) { logger.Fatal("unknown category", name) } r, ok := unicode.Categories[name] - if !ok { - logger.Fatal("unknown table", name) + if !ok && len(name) > 1 { + logger.Fatalf("unknown table %q", name) } - if name == "letter" { - verifyRange(name, letterOp, r) + if len(name) == 1 { + verifyRange(name, func(code int) bool { return categoryOp(code, name[0]) }, r) } else { verifyRange( name, @@ -487,11 +515,16 @@ func fullCategoryTest(list []string) { } func verifyRange(name string, inCategory Op, table *unicode.RangeTable) { + count := 0 for i := range chars { web := inCategory(i) pkg := unicode.Is(table, i) if web != pkg { fmt.Fprintf(os.Stderr, "%s: %U: web=%t pkg=%t\n", name, i, web, pkg) + count++ + if count > 10 { + break + } } } } @@ -882,6 +915,42 @@ func fullCaseTest() { } } +func printLatinProperties() { + if *test { + return + } + fmt.Println("var properties = [MaxLatin1+1]uint8{") + for code := 0; code <= unicode.MaxLatin1; code++ { + var property string + switch chars[code].category { + case "Cc", "": // NUL has no category. + property = "pC" + case "Cf": // soft hyphen, unique category, not printable. + property = "0" + case "Ll": + property = "pLl | pp" + case "Lu": + property = "pLu | pp" + case "Nd", "No": + property = "pN | pp" + case "Pc", "Pd", "Pe", "Pf", "Pi", "Po", "Ps": + property = "pP | pp" + case "Sc", "Sk", "Sm", "So": + property = "pS | pp" + case "Zs": + property = "pZ" + default: + logger.Fatalf("%U has unknown category %q", code, chars[code].category) + } + // Special case + if code == ' ' { + property = "pZ | pp" + } + fmt.Printf("\t0x%.2X: %s, // %q\n", code, property, code) + } + fmt.Println("}") +} + var range16Count = 0 // Number of entries in the 16-bit range tables. var range32Count = 0 // Number of entries in the 32-bit range tables. diff --git a/src/pkg/unicode/script_test.go b/src/pkg/unicode/script_test.go index ff452b75c..b37ad1836 100644 --- a/src/pkg/unicode/script_test.go +++ b/src/pkg/unicode/script_test.go @@ -149,7 +149,14 @@ var inCategoryTest = []T{ {0x2028, "Zl"}, {0x2029, "Zp"}, {0x202f, "Zs"}, - {0x04aa, "letter"}, + // Unifieds. + {0x04aa, "L"}, + {0x0009, "C"}, + {0x1712, "M"}, + {0x0031, "N"}, + {0x00bb, "P"}, + {0x00a2, "S"}, + {0x00a0, "Z"}, } var inPropTest = []T{ @@ -197,13 +204,13 @@ func TestScripts(t *testing.T) { t.Fatal(test.script, "not a known script") } if !Is(Scripts[test.script], test.rune) { - t.Errorf("IsScript(%#x, %s) = false, want true", test.rune, test.script) + t.Errorf("IsScript(%U, %s) = false, want true", test.rune, test.script) } notTested[test.script] = false, false } for _, test := range outTest { if Is(Scripts[test.script], test.rune) { - t.Errorf("IsScript(%#x, %s) = true, want false", test.rune, test.script) + t.Errorf("IsScript(%U, %s) = true, want false", test.rune, test.script) } } for k := range notTested { @@ -221,7 +228,7 @@ func TestCategories(t *testing.T) { t.Fatal(test.script, "not a known category") } if !Is(Categories[test.script], test.rune) { - t.Errorf("IsCategory(%#x, %s) = false, want true", test.rune, test.script) + t.Errorf("IsCategory(%U, %s) = false, want true", test.rune, test.script) } notTested[test.script] = false, false } @@ -240,7 +247,7 @@ func TestProperties(t *testing.T) { t.Fatal(test.script, "not a known prop") } if !Is(Properties[test.script], test.rune) { - t.Errorf("IsCategory(%#x, %s) = false, want true", test.rune, test.script) + t.Errorf("IsCategory(%U, %s) = false, want true", test.rune, test.script) } notTested[test.script] = false, false } diff --git a/src/pkg/unicode/tables.go b/src/pkg/unicode/tables.go index fc2bdd8d2..32681a8c0 100644 --- a/src/pkg/unicode/tables.go +++ b/src/pkg/unicode/tables.go @@ -9,36 +9,42 @@ const Version = "6.0.0" // Categories is the set of Unicode data tables. var Categories = map[string]*RangeTable{ - "Lm": Lm, - "Ll": Ll, - "Me": Me, - "Mc": Mc, - "Mn": Mn, - "Zl": Zl, - "letter": letter, - "Zp": Zp, - "Zs": Zs, - "Cs": Cs, - "Co": Co, - "Cf": Cf, - "Cc": Cc, - "Po": Po, - "Pi": Pi, - "Pf": Pf, - "Pe": Pe, - "Pd": Pd, - "Pc": Pc, - "Ps": Ps, - "Nd": Nd, - "Nl": Nl, - "No": No, - "So": So, - "Sm": Sm, - "Sk": Sk, - "Sc": Sc, - "Lu": Lu, - "Lt": Lt, - "Lo": Lo, + "Lm": Lm, + "Ll": Ll, + "C": C, + "M": M, + "L": L, + "N": N, + "P": P, + "S": S, + "Z": Z, + "Me": Me, + "Mc": Mc, + "Mn": Mn, + "Zl": Zl, + "Zp": Zp, + "Zs": Zs, + "Cs": Cs, + "Co": Co, + "Cf": Cf, + "Cc": Cc, + "Po": Po, + "Pi": Pi, + "Pf": Pf, + "Pe": Pe, + "Pd": Pd, + "Pc": Pc, + "Ps": Ps, + "Nd": Nd, + "Nl": Nl, + "No": No, + "So": So, + "Sm": Sm, + "Sk": Sk, + "Sc": Sc, + "Lu": Lu, + "Lt": Lt, + "Lo": Lo, } var _Lm = &RangeTable{ @@ -226,127 +232,36 @@ var _Ll = &RangeTable{ }, } -var _Me = &RangeTable{ +var _C = &RangeTable{ R16: []Range16{ - {0x0488, 0x0489, 1}, - {0x20dd, 0x20e0, 1}, - {0x20e2, 0x20e4, 1}, - {0xa670, 0xa672, 1}, - }, -} - -var _Mc = &RangeTable{ - R16: []Range16{ - {0x0903, 0x093b, 56}, - {0x093e, 0x0940, 1}, - {0x0949, 0x094c, 1}, - {0x094e, 0x094f, 1}, - {0x0982, 0x0983, 1}, - {0x09be, 0x09c0, 1}, - {0x09c7, 0x09c8, 1}, - {0x09cb, 0x09cc, 1}, - {0x09d7, 0x0a03, 44}, - {0x0a3e, 0x0a40, 1}, - {0x0a83, 0x0abe, 59}, - {0x0abf, 0x0ac0, 1}, - {0x0ac9, 0x0acb, 2}, - {0x0acc, 0x0b02, 54}, - {0x0b03, 0x0b3e, 59}, - {0x0b40, 0x0b47, 7}, - {0x0b48, 0x0b4b, 3}, - {0x0b4c, 0x0b57, 11}, - {0x0bbe, 0x0bbf, 1}, - {0x0bc1, 0x0bc2, 1}, - {0x0bc6, 0x0bc8, 1}, - {0x0bca, 0x0bcc, 1}, - {0x0bd7, 0x0c01, 42}, - {0x0c02, 0x0c03, 1}, - {0x0c41, 0x0c44, 1}, - {0x0c82, 0x0c83, 1}, - {0x0cbe, 0x0cc0, 2}, - {0x0cc1, 0x0cc4, 1}, - {0x0cc7, 0x0cc8, 1}, - {0x0cca, 0x0ccb, 1}, - {0x0cd5, 0x0cd6, 1}, - {0x0d02, 0x0d03, 1}, - {0x0d3e, 0x0d40, 1}, - {0x0d46, 0x0d48, 1}, - {0x0d4a, 0x0d4c, 1}, - {0x0d57, 0x0d82, 43}, - {0x0d83, 0x0dcf, 76}, - {0x0dd0, 0x0dd1, 1}, - {0x0dd8, 0x0ddf, 1}, - {0x0df2, 0x0df3, 1}, - {0x0f3e, 0x0f3f, 1}, - {0x0f7f, 0x102b, 172}, - {0x102c, 0x1031, 5}, - {0x1038, 0x103b, 3}, - {0x103c, 0x1056, 26}, - {0x1057, 0x1062, 11}, - {0x1063, 0x1064, 1}, - {0x1067, 0x106d, 1}, - {0x1083, 0x1084, 1}, - {0x1087, 0x108c, 1}, - {0x108f, 0x109a, 11}, - {0x109b, 0x109c, 1}, - {0x17b6, 0x17be, 8}, - {0x17bf, 0x17c5, 1}, - {0x17c7, 0x17c8, 1}, - {0x1923, 0x1926, 1}, - {0x1929, 0x192b, 1}, - {0x1930, 0x1931, 1}, - {0x1933, 0x1938, 1}, - {0x19b0, 0x19c0, 1}, - {0x19c8, 0x19c9, 1}, - {0x1a19, 0x1a1b, 1}, - {0x1a55, 0x1a57, 2}, - {0x1a61, 0x1a63, 2}, - {0x1a64, 0x1a6d, 9}, - {0x1a6e, 0x1a72, 1}, - {0x1b04, 0x1b35, 49}, - {0x1b3b, 0x1b3d, 2}, - {0x1b3e, 0x1b41, 1}, - {0x1b43, 0x1b44, 1}, - {0x1b82, 0x1ba1, 31}, - {0x1ba6, 0x1ba7, 1}, - {0x1baa, 0x1be7, 61}, - {0x1bea, 0x1bec, 1}, - {0x1bee, 0x1bf2, 4}, - {0x1bf3, 0x1c24, 49}, - {0x1c25, 0x1c2b, 1}, - {0x1c34, 0x1c35, 1}, - {0x1ce1, 0x1cf2, 17}, - {0xa823, 0xa824, 1}, - {0xa827, 0xa880, 89}, - {0xa881, 0xa8b4, 51}, - {0xa8b5, 0xa8c3, 1}, - {0xa952, 0xa953, 1}, - {0xa983, 0xa9b4, 49}, - {0xa9b5, 0xa9ba, 5}, - {0xa9bb, 0xa9bd, 2}, - {0xa9be, 0xa9c0, 1}, - {0xaa2f, 0xaa30, 1}, - {0xaa33, 0xaa34, 1}, - {0xaa4d, 0xaa7b, 46}, - {0xabe3, 0xabe4, 1}, - {0xabe6, 0xabe7, 1}, - {0xabe9, 0xabea, 1}, - {0xabec, 0xabec, 1}, + {0x0001, 0x001f, 1}, + {0x007f, 0x009f, 1}, + {0x00ad, 0x0600, 1363}, + {0x0601, 0x0603, 1}, + {0x06dd, 0x070f, 50}, + {0x17b4, 0x17b5, 1}, + {0x200b, 0x200f, 1}, + {0x202a, 0x202e, 1}, + {0x2060, 0x2064, 1}, + {0x206a, 0x206f, 1}, + {0xd800, 0xf8ff, 1}, + {0xfeff, 0xfff9, 250}, + {0xfffa, 0xfffb, 1}, }, R32: []Range32{ - {0x11000, 0x11000, 1}, - {0x11002, 0x11082, 128}, - {0x110b0, 0x110b2, 1}, - {0x110b7, 0x110b8, 1}, - {0x1d165, 0x1d166, 1}, - {0x1d16d, 0x1d172, 1}, + {0x110bd, 0x1d173, 49334}, + {0x1d174, 0x1d17a, 1}, + {0xe0001, 0xe0020, 31}, + {0xe0021, 0xe007f, 1}, + {0xf0000, 0xffffd, 1}, + {0x100000, 0x10fffd, 1}, }, } -var _Mn = &RangeTable{ +var _M = &RangeTable{ R16: []Range16{ {0x0300, 0x036f, 1}, - {0x0483, 0x0487, 1}, + {0x0483, 0x0489, 1}, {0x0591, 0x05bd, 1}, {0x05bf, 0x05c1, 2}, {0x05c2, 0x05c4, 2}, @@ -367,49 +282,69 @@ var _Mn = &RangeTable{ {0x0825, 0x0827, 1}, {0x0829, 0x082d, 1}, {0x0859, 0x085b, 1}, - {0x0900, 0x0902, 1}, - {0x093a, 0x093c, 2}, - {0x0941, 0x0948, 1}, - {0x094d, 0x0951, 4}, - {0x0952, 0x0957, 1}, + {0x0900, 0x0903, 1}, + {0x093a, 0x093c, 1}, + {0x093e, 0x094f, 1}, + {0x0951, 0x0957, 1}, {0x0962, 0x0963, 1}, - {0x0981, 0x09bc, 59}, - {0x09c1, 0x09c4, 1}, - {0x09cd, 0x09e2, 21}, + {0x0981, 0x0983, 1}, + {0x09bc, 0x09be, 2}, + {0x09bf, 0x09c4, 1}, + {0x09c7, 0x09c8, 1}, + {0x09cb, 0x09cd, 1}, + {0x09d7, 0x09e2, 11}, {0x09e3, 0x0a01, 30}, - {0x0a02, 0x0a3c, 58}, - {0x0a41, 0x0a42, 1}, + {0x0a02, 0x0a03, 1}, + {0x0a3c, 0x0a3e, 2}, + {0x0a3f, 0x0a42, 1}, {0x0a47, 0x0a48, 1}, {0x0a4b, 0x0a4d, 1}, {0x0a51, 0x0a70, 31}, {0x0a71, 0x0a75, 4}, - {0x0a81, 0x0a82, 1}, - {0x0abc, 0x0ac1, 5}, - {0x0ac2, 0x0ac5, 1}, - {0x0ac7, 0x0ac8, 1}, - {0x0acd, 0x0ae2, 21}, - {0x0ae3, 0x0b01, 30}, - {0x0b3c, 0x0b3f, 3}, - {0x0b41, 0x0b44, 1}, - {0x0b4d, 0x0b56, 9}, + {0x0a81, 0x0a83, 1}, + {0x0abc, 0x0abe, 2}, + {0x0abf, 0x0ac5, 1}, + {0x0ac7, 0x0ac9, 1}, + {0x0acb, 0x0acd, 1}, + {0x0ae2, 0x0ae3, 1}, + {0x0b01, 0x0b03, 1}, + {0x0b3c, 0x0b3e, 2}, + {0x0b3f, 0x0b44, 1}, + {0x0b47, 0x0b48, 1}, + {0x0b4b, 0x0b4d, 1}, + {0x0b56, 0x0b57, 1}, {0x0b62, 0x0b63, 1}, - {0x0b82, 0x0bc0, 62}, - {0x0bcd, 0x0c3e, 113}, - {0x0c3f, 0x0c40, 1}, + {0x0b82, 0x0bbe, 60}, + {0x0bbf, 0x0bc2, 1}, + {0x0bc6, 0x0bc8, 1}, + {0x0bca, 0x0bcd, 1}, + {0x0bd7, 0x0c01, 42}, + {0x0c02, 0x0c03, 1}, + {0x0c3e, 0x0c44, 1}, {0x0c46, 0x0c48, 1}, {0x0c4a, 0x0c4d, 1}, {0x0c55, 0x0c56, 1}, {0x0c62, 0x0c63, 1}, - {0x0cbc, 0x0cbf, 3}, - {0x0cc6, 0x0ccc, 6}, - {0x0ccd, 0x0ce2, 21}, - {0x0ce3, 0x0d41, 94}, - {0x0d42, 0x0d44, 1}, - {0x0d4d, 0x0d62, 21}, - {0x0d63, 0x0dca, 103}, - {0x0dd2, 0x0dd4, 1}, - {0x0dd6, 0x0e31, 91}, - {0x0e34, 0x0e3a, 1}, + {0x0c82, 0x0c83, 1}, + {0x0cbc, 0x0cbe, 2}, + {0x0cbf, 0x0cc4, 1}, + {0x0cc6, 0x0cc8, 1}, + {0x0cca, 0x0ccd, 1}, + {0x0cd5, 0x0cd6, 1}, + {0x0ce2, 0x0ce3, 1}, + {0x0d02, 0x0d03, 1}, + {0x0d3e, 0x0d44, 1}, + {0x0d46, 0x0d48, 1}, + {0x0d4a, 0x0d4d, 1}, + {0x0d57, 0x0d62, 11}, + {0x0d63, 0x0d82, 31}, + {0x0d83, 0x0dca, 71}, + {0x0dcf, 0x0dd4, 1}, + {0x0dd6, 0x0dd8, 2}, + {0x0dd9, 0x0ddf, 1}, + {0x0df2, 0x0df3, 1}, + {0x0e31, 0x0e34, 3}, + {0x0e35, 0x0e3a, 1}, {0x0e47, 0x0e4e, 1}, {0x0eb1, 0x0eb4, 3}, {0x0eb5, 0x0eb9, 1}, @@ -417,94 +352,79 @@ var _Mn = &RangeTable{ {0x0ec8, 0x0ecd, 1}, {0x0f18, 0x0f19, 1}, {0x0f35, 0x0f39, 2}, - {0x0f71, 0x0f7e, 1}, - {0x0f80, 0x0f84, 1}, + {0x0f3e, 0x0f3f, 1}, + {0x0f71, 0x0f84, 1}, {0x0f86, 0x0f87, 1}, {0x0f8d, 0x0f97, 1}, {0x0f99, 0x0fbc, 1}, - {0x0fc6, 0x102d, 103}, - {0x102e, 0x1030, 1}, - {0x1032, 0x1037, 1}, - {0x1039, 0x103a, 1}, - {0x103d, 0x103e, 1}, - {0x1058, 0x1059, 1}, + {0x0fc6, 0x102b, 101}, + {0x102c, 0x103e, 1}, + {0x1056, 0x1059, 1}, {0x105e, 0x1060, 1}, + {0x1062, 0x1064, 1}, + {0x1067, 0x106d, 1}, {0x1071, 0x1074, 1}, - {0x1082, 0x1085, 3}, - {0x1086, 0x108d, 7}, - {0x109d, 0x135d, 704}, - {0x135e, 0x135f, 1}, + {0x1082, 0x108d, 1}, + {0x108f, 0x109a, 11}, + {0x109b, 0x109d, 1}, + {0x135d, 0x135f, 1}, {0x1712, 0x1714, 1}, {0x1732, 0x1734, 1}, {0x1752, 0x1753, 1}, {0x1772, 0x1773, 1}, - {0x17b7, 0x17bd, 1}, - {0x17c6, 0x17c9, 3}, - {0x17ca, 0x17d3, 1}, + {0x17b6, 0x17d3, 1}, {0x17dd, 0x180b, 46}, {0x180c, 0x180d, 1}, {0x18a9, 0x1920, 119}, - {0x1921, 0x1922, 1}, - {0x1927, 0x1928, 1}, - {0x1932, 0x1939, 7}, - {0x193a, 0x193b, 1}, - {0x1a17, 0x1a18, 1}, - {0x1a56, 0x1a58, 2}, - {0x1a59, 0x1a5e, 1}, - {0x1a60, 0x1a62, 2}, - {0x1a65, 0x1a6c, 1}, - {0x1a73, 0x1a7c, 1}, + {0x1921, 0x192b, 1}, + {0x1930, 0x193b, 1}, + {0x19b0, 0x19c0, 1}, + {0x19c8, 0x19c9, 1}, + {0x1a17, 0x1a1b, 1}, + {0x1a55, 0x1a5e, 1}, + {0x1a60, 0x1a7c, 1}, {0x1a7f, 0x1b00, 129}, - {0x1b01, 0x1b03, 1}, - {0x1b34, 0x1b36, 2}, - {0x1b37, 0x1b3a, 1}, - {0x1b3c, 0x1b42, 6}, + {0x1b01, 0x1b04, 1}, + {0x1b34, 0x1b44, 1}, {0x1b6b, 0x1b73, 1}, - {0x1b80, 0x1b81, 1}, - {0x1ba2, 0x1ba5, 1}, - {0x1ba8, 0x1ba9, 1}, - {0x1be6, 0x1be8, 2}, - {0x1be9, 0x1bed, 4}, - {0x1bef, 0x1bf1, 1}, - {0x1c2c, 0x1c33, 1}, - {0x1c36, 0x1c37, 1}, + {0x1b80, 0x1b82, 1}, + {0x1ba1, 0x1baa, 1}, + {0x1be6, 0x1bf3, 1}, + {0x1c24, 0x1c37, 1}, {0x1cd0, 0x1cd2, 1}, - {0x1cd4, 0x1ce0, 1}, - {0x1ce2, 0x1ce8, 1}, - {0x1ced, 0x1dc0, 211}, - {0x1dc1, 0x1de6, 1}, + {0x1cd4, 0x1ce8, 1}, + {0x1ced, 0x1cf2, 5}, + {0x1dc0, 0x1de6, 1}, {0x1dfc, 0x1dff, 1}, - {0x20d0, 0x20dc, 1}, - {0x20e1, 0x20e5, 4}, - {0x20e6, 0x20f0, 1}, + {0x20d0, 0x20f0, 1}, {0x2cef, 0x2cf1, 1}, {0x2d7f, 0x2de0, 97}, {0x2de1, 0x2dff, 1}, {0x302a, 0x302f, 1}, {0x3099, 0x309a, 1}, - {0xa66f, 0xa67c, 13}, - {0xa67d, 0xa6f0, 115}, - {0xa6f1, 0xa802, 273}, - {0xa806, 0xa80b, 5}, - {0xa825, 0xa826, 1}, - {0xa8c4, 0xa8e0, 28}, - {0xa8e1, 0xa8f1, 1}, + {0xa66f, 0xa672, 1}, + {0xa67c, 0xa67d, 1}, + {0xa6f0, 0xa6f1, 1}, + {0xa802, 0xa806, 4}, + {0xa80b, 0xa823, 24}, + {0xa824, 0xa827, 1}, + {0xa880, 0xa881, 1}, + {0xa8b4, 0xa8c4, 1}, + {0xa8e0, 0xa8f1, 1}, {0xa926, 0xa92d, 1}, - {0xa947, 0xa951, 1}, - {0xa980, 0xa982, 1}, - {0xa9b3, 0xa9b6, 3}, - {0xa9b7, 0xa9b9, 1}, - {0xa9bc, 0xaa29, 109}, - {0xaa2a, 0xaa2e, 1}, - {0xaa31, 0xaa32, 1}, - {0xaa35, 0xaa36, 1}, + {0xa947, 0xa953, 1}, + {0xa980, 0xa983, 1}, + {0xa9b3, 0xa9c0, 1}, + {0xaa29, 0xaa36, 1}, {0xaa43, 0xaa4c, 9}, + {0xaa4d, 0xaa7b, 46}, {0xaab0, 0xaab2, 2}, {0xaab3, 0xaab4, 1}, {0xaab7, 0xaab8, 1}, {0xaabe, 0xaabf, 1}, - {0xaac1, 0xabe5, 292}, - {0xabe8, 0xabed, 5}, + {0xaac1, 0xabe3, 290}, + {0xabe4, 0xabea, 1}, + {0xabec, 0xabed, 1}, {0xfb1e, 0xfe00, 738}, {0xfe01, 0xfe0f, 1}, {0xfe20, 0xfe26, 1}, @@ -515,12 +435,13 @@ var _Mn = &RangeTable{ {0x10a05, 0x10a06, 1}, {0x10a0c, 0x10a0f, 1}, {0x10a38, 0x10a3a, 1}, - {0x10a3f, 0x11001, 1474}, + {0x10a3f, 0x11000, 1473}, + {0x11001, 0x11002, 1}, {0x11038, 0x11046, 1}, - {0x11080, 0x11081, 1}, - {0x110b3, 0x110b6, 1}, - {0x110b9, 0x110ba, 1}, - {0x1d167, 0x1d169, 1}, + {0x11080, 0x11082, 1}, + {0x110b0, 0x110ba, 1}, + {0x1d165, 0x1d169, 1}, + {0x1d16d, 0x1d172, 1}, {0x1d17b, 0x1d182, 1}, {0x1d185, 0x1d18b, 1}, {0x1d1aa, 0x1d1ad, 1}, @@ -529,13 +450,7 @@ var _Mn = &RangeTable{ }, } -var _Zl = &RangeTable{ - R16: []Range16{ - {0x2028, 0x2028, 1}, - }, -} - -var letter = &RangeTable{ +var _L = &RangeTable{ R16: []Range16{ {0x0041, 0x005a, 1}, {0x0061, 0x007a, 1}, @@ -956,6 +871,725 @@ var letter = &RangeTable{ }, } +var _N = &RangeTable{ + R16: []Range16{ + {0x0030, 0x0039, 1}, + {0x00b2, 0x00b3, 1}, + {0x00b9, 0x00bc, 3}, + {0x00bd, 0x00be, 1}, + {0x0660, 0x0669, 1}, + {0x06f0, 0x06f9, 1}, + {0x07c0, 0x07c9, 1}, + {0x0966, 0x096f, 1}, + {0x09e6, 0x09ef, 1}, + {0x09f4, 0x09f9, 1}, + {0x0a66, 0x0a6f, 1}, + {0x0ae6, 0x0aef, 1}, + {0x0b66, 0x0b6f, 1}, + {0x0b72, 0x0b77, 1}, + {0x0be6, 0x0bf2, 1}, + {0x0c66, 0x0c6f, 1}, + {0x0c78, 0x0c7e, 1}, + {0x0ce6, 0x0cef, 1}, + {0x0d66, 0x0d75, 1}, + {0x0e50, 0x0e59, 1}, + {0x0ed0, 0x0ed9, 1}, + {0x0f20, 0x0f33, 1}, + {0x1040, 0x1049, 1}, + {0x1090, 0x1099, 1}, + {0x1369, 0x137c, 1}, + {0x16ee, 0x16f0, 1}, + {0x17e0, 0x17e9, 1}, + {0x17f0, 0x17f9, 1}, + {0x1810, 0x1819, 1}, + {0x1946, 0x194f, 1}, + {0x19d0, 0x19da, 1}, + {0x1a80, 0x1a89, 1}, + {0x1a90, 0x1a99, 1}, + {0x1b50, 0x1b59, 1}, + {0x1bb0, 0x1bb9, 1}, + {0x1c40, 0x1c49, 1}, + {0x1c50, 0x1c59, 1}, + {0x2070, 0x2074, 4}, + {0x2075, 0x2079, 1}, + {0x2080, 0x2089, 1}, + {0x2150, 0x2182, 1}, + {0x2185, 0x2189, 1}, + {0x2460, 0x249b, 1}, + {0x24ea, 0x24ff, 1}, + {0x2776, 0x2793, 1}, + {0x2cfd, 0x3007, 778}, + {0x3021, 0x3029, 1}, + {0x3038, 0x303a, 1}, + {0x3192, 0x3195, 1}, + {0x3220, 0x3229, 1}, + {0x3251, 0x325f, 1}, + {0x3280, 0x3289, 1}, + {0x32b1, 0x32bf, 1}, + {0xa620, 0xa629, 1}, + {0xa6e6, 0xa6ef, 1}, + {0xa830, 0xa835, 1}, + {0xa8d0, 0xa8d9, 1}, + {0xa900, 0xa909, 1}, + {0xa9d0, 0xa9d9, 1}, + {0xaa50, 0xaa59, 1}, + {0xabf0, 0xabf9, 1}, + {0xff10, 0xff19, 1}, + }, + R32: []Range32{ + {0x10107, 0x10133, 1}, + {0x10140, 0x10178, 1}, + {0x1018a, 0x10320, 406}, + {0x10321, 0x10323, 1}, + {0x10341, 0x1034a, 9}, + {0x103d1, 0x103d5, 1}, + {0x104a0, 0x104a9, 1}, + {0x10858, 0x1085f, 1}, + {0x10916, 0x1091b, 1}, + {0x10a40, 0x10a47, 1}, + {0x10a7d, 0x10a7e, 1}, + {0x10b58, 0x10b5f, 1}, + {0x10b78, 0x10b7f, 1}, + {0x10e60, 0x10e7e, 1}, + {0x11052, 0x1106f, 1}, + {0x12400, 0x12462, 1}, + {0x1d360, 0x1d371, 1}, + {0x1d7ce, 0x1d7ff, 1}, + {0x1f100, 0x1f10a, 1}, + }, +} + +var _P = &RangeTable{ + R16: []Range16{ + {0x0021, 0x0023, 1}, + {0x0025, 0x002a, 1}, + {0x002c, 0x002f, 1}, + {0x003a, 0x003b, 1}, + {0x003f, 0x0040, 1}, + {0x005b, 0x005d, 1}, + {0x005f, 0x007b, 28}, + {0x007d, 0x00a1, 36}, + {0x00ab, 0x00b7, 12}, + {0x00bb, 0x00bf, 4}, + {0x037e, 0x0387, 9}, + {0x055a, 0x055f, 1}, + {0x0589, 0x058a, 1}, + {0x05be, 0x05c0, 2}, + {0x05c3, 0x05c6, 3}, + {0x05f3, 0x05f4, 1}, + {0x0609, 0x060a, 1}, + {0x060c, 0x060d, 1}, + {0x061b, 0x061e, 3}, + {0x061f, 0x066a, 75}, + {0x066b, 0x066d, 1}, + {0x06d4, 0x0700, 44}, + {0x0701, 0x070d, 1}, + {0x07f7, 0x07f9, 1}, + {0x0830, 0x083e, 1}, + {0x085e, 0x0964, 262}, + {0x0965, 0x0970, 11}, + {0x0df4, 0x0e4f, 91}, + {0x0e5a, 0x0e5b, 1}, + {0x0f04, 0x0f12, 1}, + {0x0f3a, 0x0f3d, 1}, + {0x0f85, 0x0fd0, 75}, + {0x0fd1, 0x0fd4, 1}, + {0x0fd9, 0x0fda, 1}, + {0x104a, 0x104f, 1}, + {0x10fb, 0x1361, 614}, + {0x1362, 0x1368, 1}, + {0x1400, 0x166d, 621}, + {0x166e, 0x169b, 45}, + {0x169c, 0x16eb, 79}, + {0x16ec, 0x16ed, 1}, + {0x1735, 0x1736, 1}, + {0x17d4, 0x17d6, 1}, + {0x17d8, 0x17da, 1}, + {0x1800, 0x180a, 1}, + {0x1944, 0x1945, 1}, + {0x1a1e, 0x1a1f, 1}, + {0x1aa0, 0x1aa6, 1}, + {0x1aa8, 0x1aad, 1}, + {0x1b5a, 0x1b60, 1}, + {0x1bfc, 0x1bff, 1}, + {0x1c3b, 0x1c3f, 1}, + {0x1c7e, 0x1c7f, 1}, + {0x1cd3, 0x2010, 829}, + {0x2011, 0x2027, 1}, + {0x2030, 0x2043, 1}, + {0x2045, 0x2051, 1}, + {0x2053, 0x205e, 1}, + {0x207d, 0x207e, 1}, + {0x208d, 0x208e, 1}, + {0x2329, 0x232a, 1}, + {0x2768, 0x2775, 1}, + {0x27c5, 0x27c6, 1}, + {0x27e6, 0x27ef, 1}, + {0x2983, 0x2998, 1}, + {0x29d8, 0x29db, 1}, + {0x29fc, 0x29fd, 1}, + {0x2cf9, 0x2cfc, 1}, + {0x2cfe, 0x2cff, 1}, + {0x2d70, 0x2e00, 144}, + {0x2e01, 0x2e2e, 1}, + {0x2e30, 0x2e31, 1}, + {0x3001, 0x3003, 1}, + {0x3008, 0x3011, 1}, + {0x3014, 0x301f, 1}, + {0x3030, 0x303d, 13}, + {0x30a0, 0x30fb, 91}, + {0xa4fe, 0xa4ff, 1}, + {0xa60d, 0xa60f, 1}, + {0xa673, 0xa67e, 11}, + {0xa6f2, 0xa6f7, 1}, + {0xa874, 0xa877, 1}, + {0xa8ce, 0xa8cf, 1}, + {0xa8f8, 0xa8fa, 1}, + {0xa92e, 0xa92f, 1}, + {0xa95f, 0xa9c1, 98}, + {0xa9c2, 0xa9cd, 1}, + {0xa9de, 0xa9df, 1}, + {0xaa5c, 0xaa5f, 1}, + {0xaade, 0xaadf, 1}, + {0xabeb, 0xfd3e, 20819}, + {0xfd3f, 0xfe10, 209}, + {0xfe11, 0xfe19, 1}, + {0xfe30, 0xfe52, 1}, + {0xfe54, 0xfe61, 1}, + {0xfe63, 0xfe68, 5}, + {0xfe6a, 0xfe6b, 1}, + {0xff01, 0xff03, 1}, + {0xff05, 0xff0a, 1}, + {0xff0c, 0xff0f, 1}, + {0xff1a, 0xff1b, 1}, + {0xff1f, 0xff20, 1}, + {0xff3b, 0xff3d, 1}, + {0xff3f, 0xff5b, 28}, + {0xff5d, 0xff5f, 2}, + {0xff60, 0xff65, 1}, + }, + R32: []Range32{ + {0x10100, 0x10101, 1}, + {0x1039f, 0x103d0, 49}, + {0x10857, 0x1091f, 200}, + {0x1093f, 0x10a50, 273}, + {0x10a51, 0x10a58, 1}, + {0x10a7f, 0x10b39, 186}, + {0x10b3a, 0x10b3f, 1}, + {0x11047, 0x1104d, 1}, + {0x110bb, 0x110bc, 1}, + {0x110be, 0x110c1, 1}, + {0x12470, 0x12473, 1}, + }, +} + +var _S = &RangeTable{ + R16: []Range16{ + {0x0024, 0x002b, 7}, + {0x003c, 0x003e, 1}, + {0x005e, 0x0060, 2}, + {0x007c, 0x007e, 2}, + {0x00a2, 0x00a9, 1}, + {0x00ac, 0x00ae, 2}, + {0x00af, 0x00b1, 1}, + {0x00b4, 0x00b8, 2}, + {0x00d7, 0x00f7, 32}, + {0x02c2, 0x02c5, 1}, + {0x02d2, 0x02df, 1}, + {0x02e5, 0x02eb, 1}, + {0x02ed, 0x02ef, 2}, + {0x02f0, 0x02ff, 1}, + {0x0375, 0x0384, 15}, + {0x0385, 0x03f6, 113}, + {0x0482, 0x0606, 388}, + {0x0607, 0x0608, 1}, + {0x060b, 0x060e, 3}, + {0x060f, 0x06de, 207}, + {0x06e9, 0x06fd, 20}, + {0x06fe, 0x07f6, 248}, + {0x09f2, 0x09f3, 1}, + {0x09fa, 0x09fb, 1}, + {0x0af1, 0x0b70, 127}, + {0x0bf3, 0x0bfa, 1}, + {0x0c7f, 0x0d79, 250}, + {0x0e3f, 0x0f01, 194}, + {0x0f02, 0x0f03, 1}, + {0x0f13, 0x0f17, 1}, + {0x0f1a, 0x0f1f, 1}, + {0x0f34, 0x0f38, 2}, + {0x0fbe, 0x0fc5, 1}, + {0x0fc7, 0x0fcc, 1}, + {0x0fce, 0x0fcf, 1}, + {0x0fd5, 0x0fd8, 1}, + {0x109e, 0x109f, 1}, + {0x1360, 0x1390, 48}, + {0x1391, 0x1399, 1}, + {0x17db, 0x1940, 357}, + {0x19de, 0x19ff, 1}, + {0x1b61, 0x1b6a, 1}, + {0x1b74, 0x1b7c, 1}, + {0x1fbd, 0x1fbf, 2}, + {0x1fc0, 0x1fc1, 1}, + {0x1fcd, 0x1fcf, 1}, + {0x1fdd, 0x1fdf, 1}, + {0x1fed, 0x1fef, 1}, + {0x1ffd, 0x1ffe, 1}, + {0x2044, 0x2052, 14}, + {0x207a, 0x207c, 1}, + {0x208a, 0x208c, 1}, + {0x20a0, 0x20b9, 1}, + {0x2100, 0x2101, 1}, + {0x2103, 0x2106, 1}, + {0x2108, 0x2109, 1}, + {0x2114, 0x2116, 2}, + {0x2117, 0x2118, 1}, + {0x211e, 0x2123, 1}, + {0x2125, 0x2129, 2}, + {0x212e, 0x213a, 12}, + {0x213b, 0x2140, 5}, + {0x2141, 0x2144, 1}, + {0x214a, 0x214d, 1}, + {0x214f, 0x2190, 65}, + {0x2191, 0x2328, 1}, + {0x232b, 0x23f3, 1}, + {0x2400, 0x2426, 1}, + {0x2440, 0x244a, 1}, + {0x249c, 0x24e9, 1}, + {0x2500, 0x26ff, 1}, + {0x2701, 0x2767, 1}, + {0x2794, 0x27c4, 1}, + {0x27c7, 0x27ca, 1}, + {0x27cc, 0x27ce, 2}, + {0x27cf, 0x27e5, 1}, + {0x27f0, 0x2982, 1}, + {0x2999, 0x29d7, 1}, + {0x29dc, 0x29fb, 1}, + {0x29fe, 0x2b4c, 1}, + {0x2b50, 0x2b59, 1}, + {0x2ce5, 0x2cea, 1}, + {0x2e80, 0x2e99, 1}, + {0x2e9b, 0x2ef3, 1}, + {0x2f00, 0x2fd5, 1}, + {0x2ff0, 0x2ffb, 1}, + {0x3004, 0x3012, 14}, + {0x3013, 0x3020, 13}, + {0x3036, 0x3037, 1}, + {0x303e, 0x303f, 1}, + {0x309b, 0x309c, 1}, + {0x3190, 0x3191, 1}, + {0x3196, 0x319f, 1}, + {0x31c0, 0x31e3, 1}, + {0x3200, 0x321e, 1}, + {0x322a, 0x3250, 1}, + {0x3260, 0x327f, 1}, + {0x328a, 0x32b0, 1}, + {0x32c0, 0x32fe, 1}, + {0x3300, 0x33ff, 1}, + {0x4dc0, 0x4dff, 1}, + {0xa490, 0xa4c6, 1}, + {0xa700, 0xa716, 1}, + {0xa720, 0xa721, 1}, + {0xa789, 0xa78a, 1}, + {0xa828, 0xa82b, 1}, + {0xa836, 0xa839, 1}, + {0xaa77, 0xaa79, 1}, + {0xfb29, 0xfbb2, 137}, + {0xfbb3, 0xfbc1, 1}, + {0xfdfc, 0xfdfd, 1}, + {0xfe62, 0xfe64, 2}, + {0xfe65, 0xfe66, 1}, + {0xfe69, 0xff04, 155}, + {0xff0b, 0xff1c, 17}, + {0xff1d, 0xff1e, 1}, + {0xff3e, 0xff40, 2}, + {0xff5c, 0xff5e, 2}, + {0xffe0, 0xffe6, 1}, + {0xffe8, 0xffee, 1}, + {0xfffc, 0xfffd, 1}, + }, + R32: []Range32{ + {0x10102, 0x10137, 53}, + {0x10138, 0x1013f, 1}, + {0x10179, 0x10189, 1}, + {0x10190, 0x1019b, 1}, + {0x101d0, 0x101fc, 1}, + {0x1d000, 0x1d0f5, 1}, + {0x1d100, 0x1d126, 1}, + {0x1d129, 0x1d164, 1}, + {0x1d16a, 0x1d16c, 1}, + {0x1d183, 0x1d184, 1}, + {0x1d18c, 0x1d1a9, 1}, + {0x1d1ae, 0x1d1dd, 1}, + {0x1d200, 0x1d241, 1}, + {0x1d245, 0x1d300, 187}, + {0x1d301, 0x1d356, 1}, + {0x1d6c1, 0x1d6db, 26}, + {0x1d6fb, 0x1d715, 26}, + {0x1d735, 0x1d74f, 26}, + {0x1d76f, 0x1d789, 26}, + {0x1d7a9, 0x1d7c3, 26}, + {0x1f000, 0x1f02b, 1}, + {0x1f030, 0x1f093, 1}, + {0x1f0a0, 0x1f0ae, 1}, + {0x1f0b1, 0x1f0be, 1}, + {0x1f0c1, 0x1f0cf, 1}, + {0x1f0d1, 0x1f0df, 1}, + {0x1f110, 0x1f12e, 1}, + {0x1f130, 0x1f169, 1}, + {0x1f170, 0x1f19a, 1}, + {0x1f1e6, 0x1f202, 1}, + {0x1f210, 0x1f23a, 1}, + {0x1f240, 0x1f248, 1}, + {0x1f250, 0x1f251, 1}, + {0x1f300, 0x1f320, 1}, + {0x1f330, 0x1f335, 1}, + {0x1f337, 0x1f37c, 1}, + {0x1f380, 0x1f393, 1}, + {0x1f3a0, 0x1f3c4, 1}, + {0x1f3c6, 0x1f3ca, 1}, + {0x1f3e0, 0x1f3f0, 1}, + {0x1f400, 0x1f43e, 1}, + {0x1f440, 0x1f442, 2}, + {0x1f443, 0x1f4f7, 1}, + {0x1f4f9, 0x1f4fc, 1}, + {0x1f500, 0x1f53d, 1}, + {0x1f550, 0x1f567, 1}, + {0x1f5fb, 0x1f5ff, 1}, + {0x1f601, 0x1f610, 1}, + {0x1f612, 0x1f614, 1}, + {0x1f616, 0x1f61c, 2}, + {0x1f61d, 0x1f61e, 1}, + {0x1f620, 0x1f625, 1}, + {0x1f628, 0x1f62b, 1}, + {0x1f62d, 0x1f630, 3}, + {0x1f631, 0x1f633, 1}, + {0x1f635, 0x1f640, 1}, + {0x1f645, 0x1f64f, 1}, + {0x1f680, 0x1f6c5, 1}, + {0x1f700, 0x1f773, 1}, + }, +} + +var _Z = &RangeTable{ + R16: []Range16{ + {0x0020, 0x00a0, 128}, + {0x1680, 0x180e, 398}, + {0x2000, 0x200a, 1}, + {0x2028, 0x2029, 1}, + {0x202f, 0x205f, 48}, + {0x3000, 0x3000, 1}, + }, +} + +var _Me = &RangeTable{ + R16: []Range16{ + {0x0488, 0x0489, 1}, + {0x20dd, 0x20e0, 1}, + {0x20e2, 0x20e4, 1}, + {0xa670, 0xa672, 1}, + }, +} + +var _Mc = &RangeTable{ + R16: []Range16{ + {0x0903, 0x093b, 56}, + {0x093e, 0x0940, 1}, + {0x0949, 0x094c, 1}, + {0x094e, 0x094f, 1}, + {0x0982, 0x0983, 1}, + {0x09be, 0x09c0, 1}, + {0x09c7, 0x09c8, 1}, + {0x09cb, 0x09cc, 1}, + {0x09d7, 0x0a03, 44}, + {0x0a3e, 0x0a40, 1}, + {0x0a83, 0x0abe, 59}, + {0x0abf, 0x0ac0, 1}, + {0x0ac9, 0x0acb, 2}, + {0x0acc, 0x0b02, 54}, + {0x0b03, 0x0b3e, 59}, + {0x0b40, 0x0b47, 7}, + {0x0b48, 0x0b4b, 3}, + {0x0b4c, 0x0b57, 11}, + {0x0bbe, 0x0bbf, 1}, + {0x0bc1, 0x0bc2, 1}, + {0x0bc6, 0x0bc8, 1}, + {0x0bca, 0x0bcc, 1}, + {0x0bd7, 0x0c01, 42}, + {0x0c02, 0x0c03, 1}, + {0x0c41, 0x0c44, 1}, + {0x0c82, 0x0c83, 1}, + {0x0cbe, 0x0cc0, 2}, + {0x0cc1, 0x0cc4, 1}, + {0x0cc7, 0x0cc8, 1}, + {0x0cca, 0x0ccb, 1}, + {0x0cd5, 0x0cd6, 1}, + {0x0d02, 0x0d03, 1}, + {0x0d3e, 0x0d40, 1}, + {0x0d46, 0x0d48, 1}, + {0x0d4a, 0x0d4c, 1}, + {0x0d57, 0x0d82, 43}, + {0x0d83, 0x0dcf, 76}, + {0x0dd0, 0x0dd1, 1}, + {0x0dd8, 0x0ddf, 1}, + {0x0df2, 0x0df3, 1}, + {0x0f3e, 0x0f3f, 1}, + {0x0f7f, 0x102b, 172}, + {0x102c, 0x1031, 5}, + {0x1038, 0x103b, 3}, + {0x103c, 0x1056, 26}, + {0x1057, 0x1062, 11}, + {0x1063, 0x1064, 1}, + {0x1067, 0x106d, 1}, + {0x1083, 0x1084, 1}, + {0x1087, 0x108c, 1}, + {0x108f, 0x109a, 11}, + {0x109b, 0x109c, 1}, + {0x17b6, 0x17be, 8}, + {0x17bf, 0x17c5, 1}, + {0x17c7, 0x17c8, 1}, + {0x1923, 0x1926, 1}, + {0x1929, 0x192b, 1}, + {0x1930, 0x1931, 1}, + {0x1933, 0x1938, 1}, + {0x19b0, 0x19c0, 1}, + {0x19c8, 0x19c9, 1}, + {0x1a19, 0x1a1b, 1}, + {0x1a55, 0x1a57, 2}, + {0x1a61, 0x1a63, 2}, + {0x1a64, 0x1a6d, 9}, + {0x1a6e, 0x1a72, 1}, + {0x1b04, 0x1b35, 49}, + {0x1b3b, 0x1b3d, 2}, + {0x1b3e, 0x1b41, 1}, + {0x1b43, 0x1b44, 1}, + {0x1b82, 0x1ba1, 31}, + {0x1ba6, 0x1ba7, 1}, + {0x1baa, 0x1be7, 61}, + {0x1bea, 0x1bec, 1}, + {0x1bee, 0x1bf2, 4}, + {0x1bf3, 0x1c24, 49}, + {0x1c25, 0x1c2b, 1}, + {0x1c34, 0x1c35, 1}, + {0x1ce1, 0x1cf2, 17}, + {0xa823, 0xa824, 1}, + {0xa827, 0xa880, 89}, + {0xa881, 0xa8b4, 51}, + {0xa8b5, 0xa8c3, 1}, + {0xa952, 0xa953, 1}, + {0xa983, 0xa9b4, 49}, + {0xa9b5, 0xa9ba, 5}, + {0xa9bb, 0xa9bd, 2}, + {0xa9be, 0xa9c0, 1}, + {0xaa2f, 0xaa30, 1}, + {0xaa33, 0xaa34, 1}, + {0xaa4d, 0xaa7b, 46}, + {0xabe3, 0xabe4, 1}, + {0xabe6, 0xabe7, 1}, + {0xabe9, 0xabea, 1}, + {0xabec, 0xabec, 1}, + }, + R32: []Range32{ + {0x11000, 0x11000, 1}, + {0x11002, 0x11082, 128}, + {0x110b0, 0x110b2, 1}, + {0x110b7, 0x110b8, 1}, + {0x1d165, 0x1d166, 1}, + {0x1d16d, 0x1d172, 1}, + }, +} + +var _Mn = &RangeTable{ + R16: []Range16{ + {0x0300, 0x036f, 1}, + {0x0483, 0x0487, 1}, + {0x0591, 0x05bd, 1}, + {0x05bf, 0x05c1, 2}, + {0x05c2, 0x05c4, 2}, + {0x05c5, 0x05c7, 2}, + {0x0610, 0x061a, 1}, + {0x064b, 0x065f, 1}, + {0x0670, 0x06d6, 102}, + {0x06d7, 0x06dc, 1}, + {0x06df, 0x06e4, 1}, + {0x06e7, 0x06e8, 1}, + {0x06ea, 0x06ed, 1}, + {0x0711, 0x0730, 31}, + {0x0731, 0x074a, 1}, + {0x07a6, 0x07b0, 1}, + {0x07eb, 0x07f3, 1}, + {0x0816, 0x0819, 1}, + {0x081b, 0x0823, 1}, + {0x0825, 0x0827, 1}, + {0x0829, 0x082d, 1}, + {0x0859, 0x085b, 1}, + {0x0900, 0x0902, 1}, + {0x093a, 0x093c, 2}, + {0x0941, 0x0948, 1}, + {0x094d, 0x0951, 4}, + {0x0952, 0x0957, 1}, + {0x0962, 0x0963, 1}, + {0x0981, 0x09bc, 59}, + {0x09c1, 0x09c4, 1}, + {0x09cd, 0x09e2, 21}, + {0x09e3, 0x0a01, 30}, + {0x0a02, 0x0a3c, 58}, + {0x0a41, 0x0a42, 1}, + {0x0a47, 0x0a48, 1}, + {0x0a4b, 0x0a4d, 1}, + {0x0a51, 0x0a70, 31}, + {0x0a71, 0x0a75, 4}, + {0x0a81, 0x0a82, 1}, + {0x0abc, 0x0ac1, 5}, + {0x0ac2, 0x0ac5, 1}, + {0x0ac7, 0x0ac8, 1}, + {0x0acd, 0x0ae2, 21}, + {0x0ae3, 0x0b01, 30}, + {0x0b3c, 0x0b3f, 3}, + {0x0b41, 0x0b44, 1}, + {0x0b4d, 0x0b56, 9}, + {0x0b62, 0x0b63, 1}, + {0x0b82, 0x0bc0, 62}, + {0x0bcd, 0x0c3e, 113}, + {0x0c3f, 0x0c40, 1}, + {0x0c46, 0x0c48, 1}, + {0x0c4a, 0x0c4d, 1}, + {0x0c55, 0x0c56, 1}, + {0x0c62, 0x0c63, 1}, + {0x0cbc, 0x0cbf, 3}, + {0x0cc6, 0x0ccc, 6}, + {0x0ccd, 0x0ce2, 21}, + {0x0ce3, 0x0d41, 94}, + {0x0d42, 0x0d44, 1}, + {0x0d4d, 0x0d62, 21}, + {0x0d63, 0x0dca, 103}, + {0x0dd2, 0x0dd4, 1}, + {0x0dd6, 0x0e31, 91}, + {0x0e34, 0x0e3a, 1}, + {0x0e47, 0x0e4e, 1}, + {0x0eb1, 0x0eb4, 3}, + {0x0eb5, 0x0eb9, 1}, + {0x0ebb, 0x0ebc, 1}, + {0x0ec8, 0x0ecd, 1}, + {0x0f18, 0x0f19, 1}, + {0x0f35, 0x0f39, 2}, + {0x0f71, 0x0f7e, 1}, + {0x0f80, 0x0f84, 1}, + {0x0f86, 0x0f87, 1}, + {0x0f8d, 0x0f97, 1}, + {0x0f99, 0x0fbc, 1}, + {0x0fc6, 0x102d, 103}, + {0x102e, 0x1030, 1}, + {0x1032, 0x1037, 1}, + {0x1039, 0x103a, 1}, + {0x103d, 0x103e, 1}, + {0x1058, 0x1059, 1}, + {0x105e, 0x1060, 1}, + {0x1071, 0x1074, 1}, + {0x1082, 0x1085, 3}, + {0x1086, 0x108d, 7}, + {0x109d, 0x135d, 704}, + {0x135e, 0x135f, 1}, + {0x1712, 0x1714, 1}, + {0x1732, 0x1734, 1}, + {0x1752, 0x1753, 1}, + {0x1772, 0x1773, 1}, + {0x17b7, 0x17bd, 1}, + {0x17c6, 0x17c9, 3}, + {0x17ca, 0x17d3, 1}, + {0x17dd, 0x180b, 46}, + {0x180c, 0x180d, 1}, + {0x18a9, 0x1920, 119}, + {0x1921, 0x1922, 1}, + {0x1927, 0x1928, 1}, + {0x1932, 0x1939, 7}, + {0x193a, 0x193b, 1}, + {0x1a17, 0x1a18, 1}, + {0x1a56, 0x1a58, 2}, + {0x1a59, 0x1a5e, 1}, + {0x1a60, 0x1a62, 2}, + {0x1a65, 0x1a6c, 1}, + {0x1a73, 0x1a7c, 1}, + {0x1a7f, 0x1b00, 129}, + {0x1b01, 0x1b03, 1}, + {0x1b34, 0x1b36, 2}, + {0x1b37, 0x1b3a, 1}, + {0x1b3c, 0x1b42, 6}, + {0x1b6b, 0x1b73, 1}, + {0x1b80, 0x1b81, 1}, + {0x1ba2, 0x1ba5, 1}, + {0x1ba8, 0x1ba9, 1}, + {0x1be6, 0x1be8, 2}, + {0x1be9, 0x1bed, 4}, + {0x1bef, 0x1bf1, 1}, + {0x1c2c, 0x1c33, 1}, + {0x1c36, 0x1c37, 1}, + {0x1cd0, 0x1cd2, 1}, + {0x1cd4, 0x1ce0, 1}, + {0x1ce2, 0x1ce8, 1}, + {0x1ced, 0x1dc0, 211}, + {0x1dc1, 0x1de6, 1}, + {0x1dfc, 0x1dff, 1}, + {0x20d0, 0x20dc, 1}, + {0x20e1, 0x20e5, 4}, + {0x20e6, 0x20f0, 1}, + {0x2cef, 0x2cf1, 1}, + {0x2d7f, 0x2de0, 97}, + {0x2de1, 0x2dff, 1}, + {0x302a, 0x302f, 1}, + {0x3099, 0x309a, 1}, + {0xa66f, 0xa67c, 13}, + {0xa67d, 0xa6f0, 115}, + {0xa6f1, 0xa802, 273}, + {0xa806, 0xa80b, 5}, + {0xa825, 0xa826, 1}, + {0xa8c4, 0xa8e0, 28}, + {0xa8e1, 0xa8f1, 1}, + {0xa926, 0xa92d, 1}, + {0xa947, 0xa951, 1}, + {0xa980, 0xa982, 1}, + {0xa9b3, 0xa9b6, 3}, + {0xa9b7, 0xa9b9, 1}, + {0xa9bc, 0xaa29, 109}, + {0xaa2a, 0xaa2e, 1}, + {0xaa31, 0xaa32, 1}, + {0xaa35, 0xaa36, 1}, + {0xaa43, 0xaa4c, 9}, + {0xaab0, 0xaab2, 2}, + {0xaab3, 0xaab4, 1}, + {0xaab7, 0xaab8, 1}, + {0xaabe, 0xaabf, 1}, + {0xaac1, 0xabe5, 292}, + {0xabe8, 0xabed, 5}, + {0xfb1e, 0xfe00, 738}, + {0xfe01, 0xfe0f, 1}, + {0xfe20, 0xfe26, 1}, + }, + R32: []Range32{ + {0x101fd, 0x10a01, 2052}, + {0x10a02, 0x10a03, 1}, + {0x10a05, 0x10a06, 1}, + {0x10a0c, 0x10a0f, 1}, + {0x10a38, 0x10a3a, 1}, + {0x10a3f, 0x11001, 1474}, + {0x11038, 0x11046, 1}, + {0x11080, 0x11081, 1}, + {0x110b3, 0x110b6, 1}, + {0x110b9, 0x110ba, 1}, + {0x1d167, 0x1d169, 1}, + {0x1d17b, 0x1d182, 1}, + {0x1d185, 0x1d18b, 1}, + {0x1d1aa, 0x1d1ad, 1}, + {0x1d242, 0x1d244, 1}, + {0xe0100, 0xe01ef, 1}, + }, +} + +var _Zl = &RangeTable{ + R16: []Range16{ + {0x2028, 0x2028, 1}, + }, +} + var _Zp = &RangeTable{ R16: []Range16{ {0x2029, 0x2029, 1}, @@ -2068,40 +2702,53 @@ var _Lo = &RangeTable{ } var ( - Cc = _Cc // Cc is the set of Unicode characters in category Cc. - Cf = _Cf // Cf is the set of Unicode characters in category Cf. - Co = _Co // Co is the set of Unicode characters in category Co. - Cs = _Cs // Cs is the set of Unicode characters in category Cs. - Digit = _Nd // Digit is the set of Unicode characters with the "decimal digit" property. - Nd = _Nd // Nd is the set of Unicode characters in category Nd. - Letter = letter // Letter is the set of Unicode letters. - Lm = _Lm // Lm is the set of Unicode characters in category Lm. - Lo = _Lo // Lo is the set of Unicode characters in category Lo. - Lower = _Ll // Lower is the set of Unicode lower case letters. - Ll = _Ll // Ll is the set of Unicode characters in category Ll. - Mc = _Mc // Mc is the set of Unicode characters in category Mc. - Me = _Me // Me is the set of Unicode characters in category Me. - Mn = _Mn // Mn is the set of Unicode characters in category Mn. - Nl = _Nl // Nl is the set of Unicode characters in category Nl. - No = _No // No is the set of Unicode characters in category No. - Pc = _Pc // Pc is the set of Unicode characters in category Pc. - Pd = _Pd // Pd is the set of Unicode characters in category Pd. - Pe = _Pe // Pe is the set of Unicode characters in category Pe. - Pf = _Pf // Pf is the set of Unicode characters in category Pf. - Pi = _Pi // Pi is the set of Unicode characters in category Pi. - Po = _Po // Po is the set of Unicode characters in category Po. - Ps = _Ps // Ps is the set of Unicode characters in category Ps. - Sc = _Sc // Sc is the set of Unicode characters in category Sc. - Sk = _Sk // Sk is the set of Unicode characters in category Sk. - Sm = _Sm // Sm is the set of Unicode characters in category Sm. - So = _So // So is the set of Unicode characters in category So. - Title = _Lt // Title is the set of Unicode title case letters. - Lt = _Lt // Lt is the set of Unicode characters in category Lt. - Upper = _Lu // Upper is the set of Unicode upper case letters. - Lu = _Lu // Lu is the set of Unicode characters in category Lu. - Zl = _Zl // Zl is the set of Unicode characters in category Zl. - Zp = _Zp // Zp is the set of Unicode characters in category Zp. - Zs = _Zs // Zs is the set of Unicode characters in category Zs. + Cc = _Cc // Cc is the set of Unicode characters in category Cc. + Cf = _Cf // Cf is the set of Unicode characters in category Cf. + Co = _Co // Co is the set of Unicode characters in category Co. + Cs = _Cs // Cs is the set of Unicode characters in category Cs. + Digit = _Nd // Digit is the set of Unicode characters with the "decimal digit" property. + Nd = _Nd // Nd is the set of Unicode characters in category Nd. + Letter = _L // Letter/L is the set of Unicode letters, category L. + L = _L + Lm = _Lm // Lm is the set of Unicode characters in category Lm. + Lo = _Lo // Lo is the set of Unicode characters in category Lo. + Lower = _Ll // Lower is the set of Unicode lower case letters. + Ll = _Ll // Ll is the set of Unicode characters in category Ll. + Mark = _M // Mark/M is the set of Unicode mark characters, category M. + M = _M + Mc = _Mc // Mc is the set of Unicode characters in category Mc. + Me = _Me // Me is the set of Unicode characters in category Me. + Mn = _Mn // Mn is the set of Unicode characters in category Mn. + Nl = _Nl // Nl is the set of Unicode characters in category Nl. + No = _No // No is the set of Unicode characters in category No. + Number = _N // Number/N is the set of Unicode number characters, category N. + N = _N + Other = _C // Other/C is the set of Unicode control and special characters, category C. + C = _C + Pc = _Pc // Pc is the set of Unicode characters in category Pc. + Pd = _Pd // Pd is the set of Unicode characters in category Pd. + Pe = _Pe // Pe is the set of Unicode characters in category Pe. + Pf = _Pf // Pf is the set of Unicode characters in category Pf. + Pi = _Pi // Pi is the set of Unicode characters in category Pi. + Po = _Po // Po is the set of Unicode characters in category Po. + Ps = _Ps // Ps is the set of Unicode characters in category Ps. + Punct = _P // Punct/P is the set of Unicode punctuation characters, category P. + P = _P + Sc = _Sc // Sc is the set of Unicode characters in category Sc. + Sk = _Sk // Sk is the set of Unicode characters in category Sk. + Sm = _Sm // Sm is the set of Unicode characters in category Sm. + So = _So // So is the set of Unicode characters in category So. + Space = _Z // Space/Z is the set of Unicode space characters, category Z. + Z = _Z + Symbol = _S // Symbol/S is the set of Unicode symbol characters, category S. + S = _S + Title = _Lt // Title is the set of Unicode title case letters. + Lt = _Lt // Lt is the set of Unicode characters in category Lt. + Upper = _Lu // Upper is the set of Unicode upper case letters. + Lu = _Lu // Lu is the set of Unicode characters in category Lu. + Zl = _Zl // Zl is the set of Unicode characters in category Zl. + Zp = _Zp // Zp is the set of Unicode characters in category Zp. + Zs = _Zs // Zs is the set of Unicode characters in category Zs. ) // Generated by running @@ -4764,6 +5411,264 @@ var _CaseRanges = []CaseRange{ {0x10400, 0x10427, d{0, 40, 0}}, {0x10428, 0x1044F, d{-40, 0, -40}}, } - -// Range entries: 2715 16-bit, 545 32-bit, 3260 total. -// Range bytes: 16290 16-bit, 6540 32-bit, 22830 total. +var properties = [MaxLatin1 + 1]uint8{ + 0x00: pC, // '\x00' + 0x01: pC, // '\x01' + 0x02: pC, // '\x02' + 0x03: pC, // '\x03' + 0x04: pC, // '\x04' + 0x05: pC, // '\x05' + 0x06: pC, // '\x06' + 0x07: pC, // '\a' + 0x08: pC, // '\b' + 0x09: pC, // '\t' + 0x0A: pC, // '\n' + 0x0B: pC, // '\v' + 0x0C: pC, // '\f' + 0x0D: pC, // '\r' + 0x0E: pC, // '\x0e' + 0x0F: pC, // '\x0f' + 0x10: pC, // '\x10' + 0x11: pC, // '\x11' + 0x12: pC, // '\x12' + 0x13: pC, // '\x13' + 0x14: pC, // '\x14' + 0x15: pC, // '\x15' + 0x16: pC, // '\x16' + 0x17: pC, // '\x17' + 0x18: pC, // '\x18' + 0x19: pC, // '\x19' + 0x1A: pC, // '\x1a' + 0x1B: pC, // '\x1b' + 0x1C: pC, // '\x1c' + 0x1D: pC, // '\x1d' + 0x1E: pC, // '\x1e' + 0x1F: pC, // '\x1f' + 0x20: pZ | pp, // ' ' + 0x21: pP | pp, // '!' + 0x22: pP | pp, // '"' + 0x23: pP | pp, // '#' + 0x24: pS | pp, // '$' + 0x25: pP | pp, // '%' + 0x26: pP | pp, // '&' + 0x27: pP | pp, // '\'' + 0x28: pP | pp, // '(' + 0x29: pP | pp, // ')' + 0x2A: pP | pp, // '*' + 0x2B: pS | pp, // '+' + 0x2C: pP | pp, // ',' + 0x2D: pP | pp, // '-' + 0x2E: pP | pp, // '.' + 0x2F: pP | pp, // '/' + 0x30: pN | pp, // '0' + 0x31: pN | pp, // '1' + 0x32: pN | pp, // '2' + 0x33: pN | pp, // '3' + 0x34: pN | pp, // '4' + 0x35: pN | pp, // '5' + 0x36: pN | pp, // '6' + 0x37: pN | pp, // '7' + 0x38: pN | pp, // '8' + 0x39: pN | pp, // '9' + 0x3A: pP | pp, // ':' + 0x3B: pP | pp, // ';' + 0x3C: pS | pp, // '<' + 0x3D: pS | pp, // '=' + 0x3E: pS | pp, // '>' + 0x3F: pP | pp, // '?' + 0x40: pP | pp, // '@' + 0x41: pLu | pp, // 'A' + 0x42: pLu | pp, // 'B' + 0x43: pLu | pp, // 'C' + 0x44: pLu | pp, // 'D' + 0x45: pLu | pp, // 'E' + 0x46: pLu | pp, // 'F' + 0x47: pLu | pp, // 'G' + 0x48: pLu | pp, // 'H' + 0x49: pLu | pp, // 'I' + 0x4A: pLu | pp, // 'J' + 0x4B: pLu | pp, // 'K' + 0x4C: pLu | pp, // 'L' + 0x4D: pLu | pp, // 'M' + 0x4E: pLu | pp, // 'N' + 0x4F: pLu | pp, // 'O' + 0x50: pLu | pp, // 'P' + 0x51: pLu | pp, // 'Q' + 0x52: pLu | pp, // 'R' + 0x53: pLu | pp, // 'S' + 0x54: pLu | pp, // 'T' + 0x55: pLu | pp, // 'U' + 0x56: pLu | pp, // 'V' + 0x57: pLu | pp, // 'W' + 0x58: pLu | pp, // 'X' + 0x59: pLu | pp, // 'Y' + 0x5A: pLu | pp, // 'Z' + 0x5B: pP | pp, // '[' + 0x5C: pP | pp, // '\\' + 0x5D: pP | pp, // ']' + 0x5E: pS | pp, // '^' + 0x5F: pP | pp, // '_' + 0x60: pS | pp, // '`' + 0x61: pLl | pp, // 'a' + 0x62: pLl | pp, // 'b' + 0x63: pLl | pp, // 'c' + 0x64: pLl | pp, // 'd' + 0x65: pLl | pp, // 'e' + 0x66: pLl | pp, // 'f' + 0x67: pLl | pp, // 'g' + 0x68: pLl | pp, // 'h' + 0x69: pLl | pp, // 'i' + 0x6A: pLl | pp, // 'j' + 0x6B: pLl | pp, // 'k' + 0x6C: pLl | pp, // 'l' + 0x6D: pLl | pp, // 'm' + 0x6E: pLl | pp, // 'n' + 0x6F: pLl | pp, // 'o' + 0x70: pLl | pp, // 'p' + 0x71: pLl | pp, // 'q' + 0x72: pLl | pp, // 'r' + 0x73: pLl | pp, // 's' + 0x74: pLl | pp, // 't' + 0x75: pLl | pp, // 'u' + 0x76: pLl | pp, // 'v' + 0x77: pLl | pp, // 'w' + 0x78: pLl | pp, // 'x' + 0x79: pLl | pp, // 'y' + 0x7A: pLl | pp, // 'z' + 0x7B: pP | pp, // '{' + 0x7C: pS | pp, // '|' + 0x7D: pP | pp, // '}' + 0x7E: pS | pp, // '~' + 0x7F: pC, // '\x7f' + 0x80: pC, // '\u0080' + 0x81: pC, // '\u0081' + 0x82: pC, // '\u0082' + 0x83: pC, // '\u0083' + 0x84: pC, // '\u0084' + 0x85: pC, // '\u0085' + 0x86: pC, // '\u0086' + 0x87: pC, // '\u0087' + 0x88: pC, // '\u0088' + 0x89: pC, // '\u0089' + 0x8A: pC, // '\u008a' + 0x8B: pC, // '\u008b' + 0x8C: pC, // '\u008c' + 0x8D: pC, // '\u008d' + 0x8E: pC, // '\u008e' + 0x8F: pC, // '\u008f' + 0x90: pC, // '\u0090' + 0x91: pC, // '\u0091' + 0x92: pC, // '\u0092' + 0x93: pC, // '\u0093' + 0x94: pC, // '\u0094' + 0x95: pC, // '\u0095' + 0x96: pC, // '\u0096' + 0x97: pC, // '\u0097' + 0x98: pC, // '\u0098' + 0x99: pC, // '\u0099' + 0x9A: pC, // '\u009a' + 0x9B: pC, // '\u009b' + 0x9C: pC, // '\u009c' + 0x9D: pC, // '\u009d' + 0x9E: pC, // '\u009e' + 0x9F: pC, // '\u009f' + 0xA0: pZ, // '\u00a0' + 0xA1: pP | pp, // '\u00a1' + 0xA2: pS | pp, // '\u00a2' + 0xA3: pS | pp, // '\u00a3' + 0xA4: pS | pp, // '\u00a4' + 0xA5: pS | pp, // '\u00a5' + 0xA6: pS | pp, // '\u00a6' + 0xA7: pS | pp, // '\u00a7' + 0xA8: pS | pp, // '\u00a8' + 0xA9: pS | pp, // '\u00a9' + 0xAA: pLl | pp, // '\u00aa' + 0xAB: pP | pp, // '\u00ab' + 0xAC: pS | pp, // '\u00ac' + 0xAD: 0, // '\u00ad' + 0xAE: pS | pp, // '\u00ae' + 0xAF: pS | pp, // '\u00af' + 0xB0: pS | pp, // '\u00b0' + 0xB1: pS | pp, // '\u00b1' + 0xB2: pN | pp, // '\u00b2' + 0xB3: pN | pp, // '\u00b3' + 0xB4: pS | pp, // '\u00b4' + 0xB5: pLl | pp, // '\u00b5' + 0xB6: pS | pp, // '\u00b6' + 0xB7: pP | pp, // '\u00b7' + 0xB8: pS | pp, // '\u00b8' + 0xB9: pN | pp, // '\u00b9' + 0xBA: pLl | pp, // '\u00ba' + 0xBB: pP | pp, // '\u00bb' + 0xBC: pN | pp, // '\u00bc' + 0xBD: pN | pp, // '\u00bd' + 0xBE: pN | pp, // '\u00be' + 0xBF: pP | pp, // '\u00bf' + 0xC0: pLu | pp, // '\u00c0' + 0xC1: pLu | pp, // '\u00c1' + 0xC2: pLu | pp, // '\u00c2' + 0xC3: pLu | pp, // '\u00c3' + 0xC4: pLu | pp, // '\u00c4' + 0xC5: pLu | pp, // '\u00c5' + 0xC6: pLu | pp, // '\u00c6' + 0xC7: pLu | pp, // '\u00c7' + 0xC8: pLu | pp, // '\u00c8' + 0xC9: pLu | pp, // '\u00c9' + 0xCA: pLu | pp, // '\u00ca' + 0xCB: pLu | pp, // '\u00cb' + 0xCC: pLu | pp, // '\u00cc' + 0xCD: pLu | pp, // '\u00cd' + 0xCE: pLu | pp, // '\u00ce' + 0xCF: pLu | pp, // '\u00cf' + 0xD0: pLu | pp, // '\u00d0' + 0xD1: pLu | pp, // '\u00d1' + 0xD2: pLu | pp, // '\u00d2' + 0xD3: pLu | pp, // '\u00d3' + 0xD4: pLu | pp, // '\u00d4' + 0xD5: pLu | pp, // '\u00d5' + 0xD6: pLu | pp, // '\u00d6' + 0xD7: pS | pp, // '\u00d7' + 0xD8: pLu | pp, // '\u00d8' + 0xD9: pLu | pp, // '\u00d9' + 0xDA: pLu | pp, // '\u00da' + 0xDB: pLu | pp, // '\u00db' + 0xDC: pLu | pp, // '\u00dc' + 0xDD: pLu | pp, // '\u00dd' + 0xDE: pLu | pp, // '\u00de' + 0xDF: pLl | pp, // '\u00df' + 0xE0: pLl | pp, // '\u00e0' + 0xE1: pLl | pp, // '\u00e1' + 0xE2: pLl | pp, // '\u00e2' + 0xE3: pLl | pp, // '\u00e3' + 0xE4: pLl | pp, // '\u00e4' + 0xE5: pLl | pp, // '\u00e5' + 0xE6: pLl | pp, // '\u00e6' + 0xE7: pLl | pp, // '\u00e7' + 0xE8: pLl | pp, // '\u00e8' + 0xE9: pLl | pp, // '\u00e9' + 0xEA: pLl | pp, // '\u00ea' + 0xEB: pLl | pp, // '\u00eb' + 0xEC: pLl | pp, // '\u00ec' + 0xED: pLl | pp, // '\u00ed' + 0xEE: pLl | pp, // '\u00ee' + 0xEF: pLl | pp, // '\u00ef' + 0xF0: pLl | pp, // '\u00f0' + 0xF1: pLl | pp, // '\u00f1' + 0xF2: pLl | pp, // '\u00f2' + 0xF3: pLl | pp, // '\u00f3' + 0xF4: pLl | pp, // '\u00f4' + 0xF5: pLl | pp, // '\u00f5' + 0xF6: pLl | pp, // '\u00f6' + 0xF7: pS | pp, // '\u00f7' + 0xF8: pLl | pp, // '\u00f8' + 0xF9: pLl | pp, // '\u00f9' + 0xFA: pLl | pp, // '\u00fa' + 0xFB: pLl | pp, // '\u00fb' + 0xFC: pLl | pp, // '\u00fc' + 0xFD: pLl | pp, // '\u00fd' + 0xFE: pLl | pp, // '\u00fe' + 0xFF: pLl | pp, // '\u00ff' +} + +// Range entries: 3190 16-bit, 657 32-bit, 3847 total. +// Range bytes: 19140 16-bit, 7884 32-bit, 27024 total. |