From e3cafeaf9292f67459ff1d186f68283bfaedf2ae Mon Sep 17 00:00:00 2001 From: Wim Date: Mon, 31 Jan 2022 00:27:37 +0100 Subject: Add dependencies/vendor (whatsapp) --- vendor/modernc.org/ccgo/v3/lib/Makefile | 136 + vendor/modernc.org/ccgo/v3/lib/ccgo.go | 2063 ++++ vendor/modernc.org/ccgo/v3/lib/cover.go | 39 + vendor/modernc.org/ccgo/v3/lib/design-notes.adoc | 91 + vendor/modernc.org/ccgo/v3/lib/dmesg.go | 42 + vendor/modernc.org/ccgo/v3/lib/etc.go | 110 + vendor/modernc.org/ccgo/v3/lib/go.go | 13056 +++++++++++++++++++++ vendor/modernc.org/ccgo/v3/lib/init.go | 553 + vendor/modernc.org/ccgo/v3/lib/mem.go | 10 + vendor/modernc.org/ccgo/v3/lib/mem_linux.go | 20 + vendor/modernc.org/ccgo/v3/lib/nodmesg.go | 12 + vendor/modernc.org/ccgo/v3/lib/stringer.go | 57 + vendor/modernc.org/ccgo/v3/lib/unconvert.sh | 4 + vendor/modernc.org/ccgo/v3/lib/util.go | 458 + 14 files changed, 16651 insertions(+) create mode 100644 vendor/modernc.org/ccgo/v3/lib/Makefile create mode 100644 vendor/modernc.org/ccgo/v3/lib/ccgo.go create mode 100644 vendor/modernc.org/ccgo/v3/lib/cover.go create mode 100644 vendor/modernc.org/ccgo/v3/lib/design-notes.adoc create mode 100644 vendor/modernc.org/ccgo/v3/lib/dmesg.go create mode 100644 vendor/modernc.org/ccgo/v3/lib/etc.go create mode 100644 vendor/modernc.org/ccgo/v3/lib/go.go create mode 100644 vendor/modernc.org/ccgo/v3/lib/init.go create mode 100644 vendor/modernc.org/ccgo/v3/lib/mem.go create mode 100644 vendor/modernc.org/ccgo/v3/lib/mem_linux.go create mode 100644 vendor/modernc.org/ccgo/v3/lib/nodmesg.go create mode 100644 vendor/modernc.org/ccgo/v3/lib/stringer.go create mode 100644 vendor/modernc.org/ccgo/v3/lib/unconvert.sh create mode 100644 vendor/modernc.org/ccgo/v3/lib/util.go (limited to 'vendor/modernc.org/ccgo/v3/lib') diff --git a/vendor/modernc.org/ccgo/v3/lib/Makefile b/vendor/modernc.org/ccgo/v3/lib/Makefile new file mode 100644 index 00000000..d8e07d0f --- /dev/null +++ b/vendor/modernc.org/ccgo/v3/lib/Makefile @@ -0,0 +1,136 @@ +# Copyright 2019 The CCGO Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +.PHONY: all bench clean cover cpu editor internalError later mem nuke todo edit devbench + +grep=--include=*.go +ngrep='TODOOK\|internalError\|testdata' +testlog=testdata/testlog-$(shell echo $$GOOS)-$(shell echo $$GOARCH)-on-$(shell go env GOOS)-$(shell go env GOARCH) + +all: + LC_ALL=C make all_log 2>&1 | tee log + +all_log: + date + go version + uname -a + ./unconvert.sh + gofmt -l -s -w *.go + GOOS=darwin GOARCH=amd64 go build -o /dev/null + GOOS=linux GOARCH=386 go build -o /dev/null + GOOS=linux GOARCH=amd64 go build -o /dev/null + GOOS=linux GOARCH=arm go build -o /dev/null + GOOS=windows GOARCH=386 go build -o /dev/null + GOOS=windows GOARCH=amd64 go build -o /dev/null + go vet 2>&1 | grep -v $(ngrep) || true + golint 2>&1 | grep -v $(ngrep) || true + make todo + misspell *.go + staticcheck | grep -v 'lexer\.go' || true + maligned || true + grep -n 'files.*, ok' log + @grep -n --color=never 'FAIL\|PASS' log + @grep -n --color=always 'FAIL' log + grep -n --color=always 'nil pointer' log + grep -c 'exit status 1' log || true + grep -c 'exit status 2' log || true + LC_ALL=C date 2>&1 | tee -a log + +test: + LC_ALL=C make test_log 2>&1 | tee $(testlog) + grep -ni --color=always fail $(testlog) || true + +test_log: + go version + uname -a + go test -v -timeout 24h + date | tee -a $(testlog) + +test_linux_amd64: + GOOS=linux GOARCH=amd64 make test + +test_linux_386: + GOOS=linux GOARCH=386 make test + +test_linux_arm: + GOOS=linux GOARCH=arm make test + +test_linux_arm64: + GOOS=linux GOARCH=arm64 make test + +test_windows_386: + go version | tee %TEMP%\testlog-windows-386 + go test -v -timeout 24h | tee -a %TEMP%\testlog-windows-386 + date /T | tee -a %TEMP%\testlog-windows-386 + time /T | tee -a %TEMP%\testlog-windows-386 + +test_windows_amd64: + go version | tee %TEMP%\testlog-windows-amd64 + go test -v -timeout 24h | tee -a %TEMP%\testlog-windows-amd64 + date /T | tee -a %TEMP%\testlog-windows-amd64 + time /T | tee -a %TEMP%\testlog-windows-amd64 + +build_all_targets: + GOOS=darwin GOARCH=amd64 go test -c -o /dev/null + GOOS=darwin GOARCH=arm64 go test -c -o /dev/null + GOOS=freebsd GOARCH=386 go test -c -o /dev/null + GOOS=freebsd GOARCH=amd64 go test -c -o /dev/null + GOOS=linux GOARCH=386 go test -c -o /dev/null + GOOS=linux GOARCH=amd64 go test -c -o /dev/null + GOOS=linux GOARCH=arm go test -c -o /dev/null + GOOS=linux GOARCH=arm64 go test -c -o /dev/null + GOOS=linux GOARCH=s390x go test -c -o /dev/null + GOOS=netbsd GOARCH=amd64 go test -c -o /dev/null + GOOS=openbsd GOARCH=amd64 go test -c -o /dev/null + GOOS=windows GOARCH=386 go test -c -o /dev/null + GOOS=windows GOARCH=amd64 go test -c -o /dev/null + +devbench: + date 2>&1 | tee log-devbench + go test -timeout 24h -dev -run @ -bench . 2>&1 | tee -a log-devbench + grep -n 'FAIL\|SKIP' log-devbench || true + +bench: + date 2>&1 | tee log-bench + go test -timeout 24h -v -run '^[^E]' -bench . 2>&1 | tee -a log-bench + grep -n 'FAIL\|SKIP' log-bench || true + +clean: + go clean + rm -f *~ *.test *.out + +cover: + t=$(shell mktemp) ; go test -coverprofile $$t && go tool cover -html $$t && unlink $$t + +cpu: clean + go test -run @ -bench . -cpuprofile cpu.out + go tool pprof -lines *.test cpu.out + +edit: + @touch log + @if [ -f "Session.vim" ]; then gvim -S & else gvim -p Makefile *.go & fi + + +editor: + gofmt -l -s -w *.go + go build -v -o $(GOPATH)/bin/ccgo modernc.org/ccgo/v3 + go test -c -o /dev/null + +later: + @grep -n $(grep) LATER * || true + @grep -n $(grep) MAYBE * || true + +mem: clean + go test -run Mem -mem -memprofile mem.out -timeout 24h + go tool pprof -lines -web -alloc_space *.test mem.out + +nuke: clean + go clean -i + +todo: + @grep -nr $(grep) ^[[:space:]]*_[[:space:]]*=[[:space:]][[:alpha:]][[:alnum:]]* * | grep -v $(ngrep) || true + @grep -nr $(grep) 'TODO\|panic' * | grep -v $(ngrep) || true + @grep -nr $(grep) BUG * | grep -v $(ngrep) || true + @grep -nr $(grep) [^[:alpha:]]println * | grep -v $(ngrep) || true + @grep -nir $(grep) 'work.*progress' || true diff --git a/vendor/modernc.org/ccgo/v3/lib/ccgo.go b/vendor/modernc.org/ccgo/v3/lib/ccgo.go new file mode 100644 index 00000000..a5ed804f --- /dev/null +++ b/vendor/modernc.org/ccgo/v3/lib/ccgo.go @@ -0,0 +1,2063 @@ +// Copyright 2020 The CCGO Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:generate stringer -output stringer.go -type=exprMode,opKind + +// Package ccgo implements the ccgo command. +package ccgo // import "modernc.org/ccgo/v3/lib" + +import ( + "bufio" + "bytes" + "encoding/csv" + "encoding/json" + "fmt" + "go/ast" + "go/build" + "go/parser" + "go/token" + "io" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "regexp" + "runtime" + "runtime/debug" + "sort" + "strconv" + "strings" + "time" + + "github.com/kballard/go-shellquote" + "golang.org/x/tools/go/packages" + "modernc.org/cc/v3" + "modernc.org/libc" + "modernc.org/opt" +) + +const ( + Version = "3.12.6-20210922111124" + + experimentsEnvVar = "CCGO_EXPERIMENT" + maxSourceLine = 1 << 20 +) + +var ( + _ = libc.Xstdin + + coverExperiment bool +) + +func init() { + s := strings.TrimSpace(os.Getenv(experimentsEnvVar)) + if s == "" { + return + } + + for _, v := range strings.Split(s, ",") { + switch strings.TrimSpace(v) { + case "cover": + coverExperiment = true + } + } +} + +//TODO CPython +//TODO Cython +//TODO gmp +//TODO gofrontend +//TODO gsl +//TODO minigmp +//TODO mpc +//TODO mpfr +//TODO pcre +//TODO pcre2 +//TODO quickjs +//TODO redis +//TODO sdl2 +//TODO wolfssl +//TODO zdat +//TODO zstd + +//TODO 2020-07-17 +// +// Fix += and friends +// +// Audit all unsafe.Pointer conversions +// +// Remove double dereferencing ** +// +// Shifts must not use n.Promote on left opearand +// +// Un-array +// +// Pass more CSmith tests. + +//TODO merge VaList slots of distinct top level statements. + +//TODO turn void +// +// a = b = c = d +// +// where all but the first and last of a, b, c, ... are declarators, into +// +// c = d +// b = c +// a = b + +//TODO define and use all tagged struct types, including inner ones, for example SQLite's SrcList_item. + +//TODO rewrite return conditionalExpression so it has no closures. Partially done. + +//TODO define and restore simple named constants. Having +// +// #define FOO 42 +// ... +// case FOO: +// +// we do not yet define FOO and generate +// +// case 42: + +//TODO do not generate a terminating semicolon for empty statements. + +//TODO replace +// +// var sqlite3_data_directory uintptr = uintptr(0) /* sqlite3.c:156345:17 */ +// +// by +// +// var sqlite3_data_directory uintptr = 0 /* sqlite3.c:156345:17 */ +// +// or +// +// var sqlite3_data_directory = uintptr(0) /* sqlite3.c:156345:17 */ + +//TODO drop all non-referenced declarators unless forced by a command line flag. + +const ( + builtin = ` +#ifdef __PTRDIFF_TYPE__ +typedef __PTRDIFF_TYPE__ ptrdiff_t; +#else +#error __PTRDIFF_TYPE__ undefined +#endif + +#ifdef __SIZE_TYPE__ +typedef __SIZE_TYPE__ size_t; +#else +#error __SIZE_TYPE__ undefined +#endif + +#ifdef __WCHAR_TYPE__ +typedef __WCHAR_TYPE__ wchar_t; +#else +#error __WCHAR_TYPE__ undefined +#endif + +#ifdef __SIZEOF_INT128__ +typedef struct { __INT64_TYPE__ lo, hi; } __int128_t; // must match modernc.org/mathutil.Int128 +typedef struct { __UINT64_TYPE__ lo, hi; } __uint128_t; // must match modernc.org/mathutil.Int128 +#endif; + +#define _FILE_OFFSET_BITS 64 +#define __FUNCTION__ __func__ +#define __PRETTY_FUNCTION__ __func__ +#define __asm __asm__ +#define __builtin_constant_p(x) __builtin_constant_p_impl(0, x) +#define __builtin_offsetof(type, member) ((__SIZE_TYPE__)&(((type*)0)->member)) +#define __builtin_va_arg(ap, type) ((type)__ccgo_va_arg(ap)) +#define __builtin_va_copy(dst, src) dst = src +#define __builtin_va_end(ap) __ccgo_va_end(ap) +#define __builtin_va_start(ap, v) __ccgo_va_start(ap) +#define __ccgo_fd_zero(set) __builtin_memset(set, 0, sizeof(fd_set)) +#define __ccgo_tcl_default_double_rounding(set) ((void)0) +#define __ccgo_tcl_ieee_double_rounding(set) ((void)0) +#define __extension__ +#define __has_include(...) __has_include_impl(#__VA_ARGS__) +#define __has_include_impl(x) +#define __inline__ inline +#define __signed signed +#define asm __asm__ +#define in6addr_any (*__ccgo_in6addr_anyp()) + +typedef void *__builtin_va_list; +typedef long double __float128; + +#if defined(__MINGW32__) || defined(__MINGW64__) +typedef __builtin_va_list va_list; +int gnu_printf(const char *format, ...); +int gnu_scanf(const char *format, ...); +int ms_printf(const char *format, ...); +int ms_scanf(const char *format, ...); +#define _VA_LIST_DEFINED +#define __extension__ +#endif + +__UINT16_TYPE__ __builtin_bswap16 (__UINT16_TYPE__ x); +__UINT32_TYPE__ __builtin_bswap32 (__UINT32_TYPE__ x); +__UINT64_TYPE__ __builtin_bswap64 (__UINT64_TYPE__ x); +char *__builtin___strcat_chk (char *dest, const char *src, size_t os); +char *__builtin___strcpy_chk (char *dest, const char *src, size_t os); +char *__builtin___strncpy_chk(char *dest, char *src, size_t n, size_t os); +char *__builtin_strchr(const char *s, int c); +char *__builtin_strcpy(char *dest, const char *src); +double __builtin_copysign ( double x, double y ); +double __builtin_copysignl (long double x, long double y ); +double __builtin_fabs(double x); +double __builtin_huge_val (void); +double __builtin_inf (void); +double __builtin_nan (const char *str); +float __builtin_copysignf ( float x, float y ); +float __builtin_fabsf(float x); +float __builtin_huge_valf (void); +float __builtin_inff (void); +float __builtin_nanf (const char *str); +int __builtin___snprintf_chk (char *s, size_t maxlen, int flag, size_t os, const char *fmt, ...); +int __builtin___sprintf_chk (char *s, int flag, size_t os, const char *fmt, ...); +int __builtin___vsnprintf_chk (char *s, size_t maxlen, int flag, size_t os, const char *fmt, __builtin_va_list ap); +int __builtin__snprintf_chk(char * str, size_t maxlen, int flag, size_t strlen, const char * format); +int __builtin_abs(int j); +int __builtin_add_overflow(); +int __builtin_clz (unsigned); +int __builtin_isunordered(double x, double y); +int __builtin_clzl (unsigned long); +int __builtin_clzll (unsigned long long); +int __builtin_constant_p_impl(int, ...); +int __builtin_getentropy(void*, size_t); +int __builtin_isnan(double); +int __builtin_memcmp(const void *s1, const void *s2, size_t n); +int __builtin_mul_overflow(); +int __builtin_popcount (unsigned int x); +int __builtin_popcountl (unsigned long x); +int __builtin_printf(const char *format, ...); +int __builtin_snprintf(char *str, size_t size, const char *format, ...); +int __builtin_sprintf(char *str, const char *format, ...); +int __builtin_strcmp(const char *s1, const char *s2); +int __builtin_sub_overflow(); +long __builtin_expect (long exp, long c); +long double __builtin_fabsl(long double x); +long double __builtin_nanl (const char *str); +long long __builtin_llabs(long long j); +size_t __builtin_object_size (void * ptr, int type); +size_t __builtin_strlen(const char *s); +void *__builtin___memcpy_chk (void *dest, const void *src, size_t n, size_t os); +void *__builtin___memmove_chk (void *dest, const void *src, size_t n, size_t os); +void *__builtin___memset_chk (void *dstpp, int c, size_t len, size_t dstlen); +void *__builtin_malloc(size_t size); +void *__builtin_memcpy(void *dest, const void *src, size_t n); +void *__builtin_memset(void *s, int c, size_t n); +void *__builtin_mmap(void *addr, size_t length, int prot, int flags, int fd, __INTPTR_TYPE__ offset); +void *__ccgo_va_arg(__builtin_va_list ap); +void __builtin_abort(void); +void __builtin_bzero(void *s, size_t n); +void __builtin_exit(int status); +void __builtin_free(void *ptr); +void __builtin_prefetch (const void *addr, ...); +void __builtin_trap (void); +void __builtin_unreachable (void); +void __ccgo_dmesg(char*, ...); +void __ccgo_va_end(__builtin_va_list ap); +void __ccgo_va_start(__builtin_va_list ap); + +#define __sync_add_and_fetch(ptr, val) \ + __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(*ptr), unsigned), \ + __sync_add_and_fetch_uint32(ptr, val), \ + __TODO__ \ + ) + +#define __sync_fetch_and_add(ptr, val) \ + __TODO__ \ + +#define __sync_sub_and_fetch(ptr, val) \ + __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(*ptr), unsigned), \ + __sync_sub_and_fetch_uint32(ptr, val), \ + __TODO__ \ + ) + +unsigned __sync_add_and_fetch_uint32(unsigned*, unsigned); +unsigned __sync_sub_and_fetch_uint32(unsigned*, unsigned); + +#ifdef __APPLE__ +int (*__darwin_check_fd_set_overflow)(int, void *, int); +#endif + +` + defaultCrt = "modernc.org/libc" +) + +func origin(skip int) string { + pc, fn, fl, _ := runtime.Caller(skip) + f := runtime.FuncForPC(pc) + var fns string + if f != nil { + fns = f.Name() + if x := strings.LastIndex(fns, "."); x > 0 { + fns = fns[x+1:] + } + } + return fmt.Sprintf("%s:%d:%s", fn, fl, fns) +} + +func todo(s string, args ...interface{}) string { + switch { + case s == "": + s = fmt.Sprintf(strings.Repeat("%v ", len(args)), args...) + default: + s = fmt.Sprintf(s, args...) + } + r := fmt.Sprintf("%s\n\tTODO %s", origin(2), s) //TODOOK + fmt.Fprintf(os.Stdout, "%s\n", r) + os.Stdout.Sync() + return r +} + +func trc(s string, args ...interface{}) string { + switch { + case s == "": + s = fmt.Sprintf(strings.Repeat("%v ", len(args)), args...) + default: + s = fmt.Sprintf(s, args...) + } + r := fmt.Sprintf("%s: TRC %s", origin(2), s) + fmt.Fprintf(os.Stderr, "%s\n", r) + os.Stderr.Sync() + return r +} + +// Task represents a compilation job. +type Task struct { + D []string // -D + I []string // -I + U []string // -U + ar string // $AR, default "ar" + arLookPath string // LookPath(ar) + args []string + asts []*cc.AST + capif string + saveConfig string // -save-config + saveConfigErr error + cc string // $CC, default "gcc" + ccLookPath string // LookPath(cc) + cdb string // foo.json, use compile DB + cfg *cc.Config + compiledb string // -compiledb + crt string + crtImportPath string // -crt-import-path + exportDefines string // -export-defines + exportEnums string // -export-enums + exportExterns string // -export-externs + exportFields string // -export-fields + exportStructs string // -export-structs + exportTypedefs string // -export-typedefs + goarch string + goos string + hide map[string]struct{} // -hide + hostConfigCmd string // -host-config-cmd + hostConfigOpts string // -host-config-opts + hostIncludes []string + hostPredefined string + hostSysIncludes []string + ignoredIncludes string // -ignored-includes + ignoredObjects map[string]struct{} // -ignore-object + imported []*imported + includedFiles map[string]struct{} + l []string // -l + loadConfig string // --load-config + o string // -o + out io.Writer + pkgName string // -pkgname + replaceFdZero string // -replace-fd-zero + replaceTclDefaultDoubleRounding string // -replace-tcl-default-double-rounding + replaceTclIeeeDoubleRounding string // -replace-tcl-default-double-rounding + scriptFn string // -script + sources []cc.Source + staticLocalsPrefix string // -static-locals-prefix + stderr io.Writer + stdout io.Writer + symSearchOrder []int // >= 0: asts[i], < 0 : imported[-i-1] + verboseCompiledb bool // -verbose-compiledb + volatiles map[cc.StringID]struct{} // -volatile + + // Path to a binary that will be called instead of executing + // Task.Main(). Intended to support TestGenerate in stable vs latest + // modes. This is _not_ supposed to be used when the Task instance is + // constructed by a ccgo _command_ (ccgo/v3) - it should never set this + // field. Only programs importing ccgo/v3/lib that opt-in into this + // feature should ever set it. + CallOutBinary string + + E bool // -E + allErrors bool // -all-errors + compiledbValid bool // -compiledb present + configSaved bool + configured bool // hostPredefined, hostIncludes, hostSysIncludes are valid + cover bool // -cover-instrumentation + coverC bool // -cover-instrumentation-c + defaultUnExport bool // -unexported-by-default + errTrace bool // -err-trace + exportDefinesValid bool // -export-defines present + exportEnumsValid bool // -export-enums present + exportExternsValid bool // -export-externs present + exportFieldsValid bool // -export-fields present + exportStructsValid bool // -export-structs present + exportTypedefsValid bool // -export-typedefs present + fullPathComments bool // -full-path-comments + funcSig bool // -func-sig + header bool // -header + ignoreUnsupportedAligment bool // -ignore-unsupported-alignment + isScripted bool + mingw bool + noCapi bool // -nocapi + nostdinc bool // -nostdinc + nostdlib bool // -nostdlib + panicStubs bool // -panic-stubs + tracePinning bool // -trace-pinning + traceTranslationUnits bool // -trace-translation-units + verifyStructs bool // -verify-structs + version bool // -version + watch bool // -watch-instrumentation + windows bool // -windows +} + +// NewTask returns a newly created Task. +func NewTask(args []string, stdout, stderr io.Writer) *Task { + if dmesgs { + dmesg("%v: %v", origin(1), args) + } + if stdout == nil { + stdout = os.Stdout + } + if stderr == nil { + stderr = os.Stderr + } + return &Task{ + args: args, + cfg: &cc.Config{ + Config3: cc.Config3{ + MaxSourceLine: maxSourceLine, + }, + DoNotTypecheckAsm: true, + EnableAssignmentCompatibilityChecking: true, + LongDoubleIsDouble: true, + SharedFunctionDefinitions: &cc.SharedFunctionDefinitions{}, + }, + ar: env("AR", "ar"), + cc: env("CC", "gcc"), + crt: "libc.", + crtImportPath: defaultCrt, + goarch: env("TARGET_GOARCH", env("GOARCH", runtime.GOARCH)), + goos: env("TARGET_GOOS", env("GOOS", runtime.GOOS)), + hide: map[string]struct{}{}, + hostConfigCmd: env("CCGO_CPP", ""), + pkgName: "main", + stderr: stderr, + stdout: stdout, + volatiles: map[cc.StringID]struct{}{}, + } +} + +func env(name, deflt string) (r string) { + r = deflt + if s := os.Getenv(name); s != "" { + r = s + } + return r +} + +// Get exported symbols from package having import path 'path'. +func (t *Task) capi(path string) (pkgName string, exports map[string]struct{}, err error) { + // defer func() { + // var a []string + // for k := range exports { + // a = append(a, k) + // } + // sort.Strings(a) + // trc("%s\n%s", path, strings.Join(a, "\n")) + // }() + var errModule, errGopath error + defer func() { + if err != nil { + a := []string{err.Error()} + if errModule != nil { + a = append(a, fmt.Sprintf("module mode error: %s", errModule)) + } + if errGopath != nil { + a = append(a, fmt.Sprintf("gopath mode error: %s", errGopath)) + } + wd, err2 := os.Getwd() + err = fmt.Errorf( + "(wd %q, %v): loading C exports from %s (GOPATH=%v GO111MODULE=%v): %v", + wd, err2, path, os.Getenv("GOPATH"), os.Getenv("GO111MODULE"), strings.Join(a, "\n\t"), + ) + } + }() + + mod := os.Getenv("GO111MODULE") + if mod == "" || mod == "on" { + var pkgs []*packages.Package + pkgs, errModule = packages.Load( + &packages.Config{ + Mode: packages.NeedFiles, + Env: append(os.Environ(), fmt.Sprintf("GOOS=%s", t.goos), fmt.Sprintf("GOARCH=%s", t.goarch)), + }, + path, + ) + switch { + case errModule == nil: + if len(pkgs) != 1 { + errModule = fmt.Errorf("expected one package, loaded %d", len(pkgs)) + break + } + + pkg := pkgs[0] + if len(pkg.Errors) != 0 { + var a []string + for _, v := range pkg.Errors { + a = append(a, v.Error()) + } + errModule = fmt.Errorf("%s", strings.Join(a, "\n")) + break + } + + return t.capi2(pkg.GoFiles) + } + } + + gopath0 := os.Getenv("GOPATH") + for _, gopath := range strings.Split(gopath0, string(os.PathListSeparator)) { + if gopath == "" || !filepath.IsAbs(gopath) { + continue + } + + ctx := build.Context{ + GOARCH: t.goarch, + GOOS: t.goos, + GOPATH: gopath, + Compiler: "gc", + } + arg := filepath.Join(gopath, "src", path) + pkg, err := ctx.ImportDir(arg, 0) + if err != nil { + errGopath = err + continue + } + + for i, v := range pkg.GoFiles { + pkg.GoFiles[i] = filepath.Join(gopath, "src", path, v) + } + return t.capi2(pkg.GoFiles) + } + return "", nil, fmt.Errorf("cannot load CAPI") +} + +func (t *Task) capi2(files []string) (pkgName string, exports map[string]struct{}, err error) { + exports = map[string]struct{}{} + base := fmt.Sprintf("capi_%s_%s.go", t.goos, t.goarch) + var fn string + for _, v := range files { + if filepath.Base(v) == base { + fn = v + break + } + } + if fn == "" { + return "", nil, fmt.Errorf("file %s not found", base) + } + + fset := token.NewFileSet() + file, err := parser.ParseFile(fset, fn, nil, 0) + if err != nil { + return "", nil, err + } + + obj, ok := file.Scope.Objects["CAPI"] + if !ok { + return "", nil, fmt.Errorf("CAPI not declared in %s", fn) + } + + switch obj.Kind { + case ast.Var: + // ok + default: + return "", nil, fmt.Errorf("unexpected CAPI object kind: %v", obj.Kind) + } + + spec, ok := obj.Decl.(*ast.ValueSpec) + if !ok { + return "", nil, fmt.Errorf("unexpected CAPI object type: %T", obj.Decl) + } + + if len(spec.Values) != 1 { + return "", nil, fmt.Errorf("expected one CAPI expression, got %v", len(spec.Values)) + } + + ast.Inspect(spec.Values[0], func(n ast.Node) bool { + if x, ok := n.(*ast.BasicLit); ok { + var key string + if key, err = strconv.Unquote(x.Value); err != nil { + err = fmt.Errorf("invalid CAPI key value: %s", x.Value) + return false + } + + exports[key] = struct{}{} + } + return true + }) + return file.Name.String(), exports, err +} + +// Main executes task. +func (t *Task) Main() (err error) { + if dmesgs { + defer func() { + if err != nil { + // trc("FAIL %p: %q: %v", t, t.args, err) + dmesg("%v: returning from Task.Main: %v", origin(1), err) + } + }() + + } + + defer func() { + if t.saveConfigErr != nil && err == nil { + err = t.saveConfigErr + } + }() + + if !t.isScripted && coverExperiment { + defer func() { + fmt.Fprintf(os.Stderr, "cover report:\n%s\n", coverReport()) + }() + } + if t.CallOutBinary != "" { + if dmesgs { + dmesg("%v: calling out '%s' instead", origin(1)) + } + cmd := exec.Command(t.CallOutBinary, t.args[1:]...) + out, err := cmd.CombinedOutput() + if err != nil { + err = fmt.Errorf("%v\n%s", err, out) + } + return err + } + + opts := opt.NewSet() + opts.Arg("D", true, func(arg, value string) error { t.D = append(t.D, value); return nil }) + opts.Arg("I", true, func(opt, arg string) error { t.I = append(t.I, arg); return nil }) + opts.Arg("U", true, func(arg, value string) error { t.U = append(t.U, value); return nil }) + opts.Arg("compiledb", false, func(arg, value string) error { t.compiledb = value; t.compiledbValid = true; return opt.Skip(nil) }) + opts.Arg("crt-import-path", false, func(arg, value string) error { t.crtImportPath = value; return nil }) + opts.Arg("export-defines", false, func(arg, value string) error { t.exportDefines = value; t.exportDefinesValid = true; return nil }) + opts.Arg("export-enums", false, func(arg, value string) error { t.exportEnums = value; t.exportEnumsValid = true; return nil }) + opts.Arg("export-externs", false, func(arg, value string) error { t.exportExterns = value; t.exportExternsValid = true; return nil }) + opts.Arg("export-fields", false, func(arg, value string) error { t.exportFields = value; t.exportFieldsValid = true; return nil }) + opts.Arg("export-structs", false, func(arg, value string) error { t.exportStructs = value; t.exportStructsValid = true; return nil }) + opts.Arg("export-typedefs", false, func(arg, value string) error { t.exportTypedefs = value; t.exportTypedefsValid = true; return nil }) + opts.Arg("host-config-cmd", false, func(arg, value string) error { t.hostConfigCmd = value; return nil }) + opts.Arg("host-config-opts", false, func(arg, value string) error { t.hostConfigOpts = value; return nil }) + opts.Arg("ignored-includes", false, func(arg, value string) error { t.ignoredIncludes = value; return nil }) + opts.Arg("pkgname", false, func(arg, value string) error { t.pkgName = value; return nil }) + opts.Arg("replace-fd-zero", false, func(arg, value string) error { t.replaceFdZero = value; return nil }) + opts.Arg("replace-tcl-default-double-rounding", false, func(arg, value string) error { t.replaceTclDefaultDoubleRounding = value; return nil }) + opts.Arg("replace-tcl-ieee-double-rounding", false, func(arg, value string) error { t.replaceTclIeeeDoubleRounding = value; return nil }) + opts.Arg("script", false, func(arg, value string) error { t.scriptFn = value; return nil }) + opts.Arg("static-locals-prefix", false, func(arg, value string) error { t.staticLocalsPrefix = value; return nil }) + + opts.Opt("E", func(opt string) error { t.E = true; return nil }) + opts.Opt("all-errors", func(opt string) error { t.allErrors = true; return nil }) + opts.Opt("cover-instrumentation", func(opt string) error { t.cover = true; return nil }) + opts.Opt("cover-instrumentation-c", func(opt string) error { t.coverC = true; return nil }) + opts.Opt("err-trace", func(opt string) error { t.errTrace = true; return nil }) + opts.Opt("full-path-comments", func(opt string) error { t.fullPathComments = true; return nil }) + opts.Opt("func-sig", func(opt string) error { t.funcSig = true; return nil }) + opts.Opt("header", func(opt string) error { t.header = true; return nil }) + opts.Opt("ignore-unsupported-alignment", func(opt string) error { t.ignoreUnsupportedAligment = true; return nil }) + opts.Opt("nocapi", func(opt string) error { t.noCapi = true; return nil }) + opts.Opt("nostdinc", func(opt string) error { t.nostdinc = true; return nil }) + opts.Opt("panic-stubs", func(opt string) error { t.panicStubs = true; return nil }) + opts.Opt("trace-pinning", func(opt string) error { t.tracePinning = true; return nil }) + opts.Opt("trace-translation-units", func(opt string) error { t.traceTranslationUnits = true; return nil }) + opts.Opt("unexported-by-default", func(opt string) error { t.defaultUnExport = true; return nil }) + opts.Opt("verbose-compiledb", func(opt string) error { t.verboseCompiledb = true; return nil }) + opts.Opt("verify-structs", func(opt string) error { t.verifyStructs = true; return nil }) + opts.Opt("version", func(opt string) error { t.version = true; return nil }) + opts.Opt("watch-instrumentation", func(opt string) error { t.watch = true; return nil }) + opts.Opt("windows", func(opt string) error { t.windows = true; return nil }) + + opts.Opt("trace-included-files", func(opt string) error { + if t.includedFiles == nil { + t.includedFiles = map[string]struct{}{} + } + prev := t.cfg.IncludeFileHandler + t.cfg.IncludeFileHandler = func(pos token.Position, pathName string) { + if prev != nil { + prev(pos, pathName) + } + if _, ok := t.includedFiles[pathName]; !ok { + t.includedFiles[pathName] = struct{}{} + fmt.Fprintf(os.Stderr, "#include %s\n", pathName) + } + } + return nil + }) + opts.Arg("ignore-object", false, func(arg, value string) error { + if t.ignoredObjects == nil { + t.ignoredObjects = map[string]struct{}{} + } + t.ignoredObjects[value] = struct{}{} + return nil + }) + opts.Arg("save-config", false, func(arg, value string) error { + if value == "" { + return nil + } + + abs, err := filepath.Abs(value) + if err != nil { + return err + } + + t.saveConfig = abs + if t.includedFiles == nil { + t.includedFiles = map[string]struct{}{} + } + prev := t.cfg.IncludeFileHandler + t.cfg.IncludeFileHandler = func(pos token.Position, pathName string) { + if prev != nil { + prev(pos, pathName) + } + if _, ok := t.includedFiles[pathName]; !ok { + t.includedFiles[pathName] = struct{}{} + full := filepath.Join(abs, pathName) + switch _, err := os.Stat(full); { + case err != nil && os.IsNotExist(err): + // ok + case err != nil: + t.saveConfigErr = err + return + default: + return + } + + b, err := ioutil.ReadFile(pathName) + if err != nil { + t.saveConfigErr = err + return + } + + dir, _ := filepath.Split(full) + if err := os.MkdirAll(dir, 0700); err != nil { + t.saveConfigErr = err + return + } + + if err := ioutil.WriteFile(full, b, 0600); err != nil { + t.saveConfigErr = err + } + } + } + return nil + }) + opts.Arg("-load-config", false, func(arg, value string) error { + if value == "" { + return nil + } + + abs, err := filepath.Abs(value) + if err != nil { + return err + } + + t.loadConfig = abs + return nil + }) + opts.Arg("volatile", false, func(arg, value string) error { + for _, v := range strings.Split(strings.TrimSpace(value), ",") { + t.volatiles[cc.String(v)] = struct{}{} + } + return nil + }) + opts.Opt("nostdlib", func(opt string) error { + t.nostdlib = true + t.crt = "" + t.crtImportPath = "" + return nil + }) + opts.Arg("hide", false, func(arg, value string) error { + value = strings.TrimSpace(value) + a := strings.Split(value, ",") + for _, v := range a { + t.hide[v] = struct{}{} + } + return nil + }) + opts.Arg("l", true, func(arg, value string) error { + value = strings.TrimSpace(value) + a := strings.Split(value, ",") + for _, v := range a { + t.l = append(t.l, v) + t.symSearchOrder = append(t.symSearchOrder, -len(t.l)) + } + return nil + }) + opts.Arg("o", false, func(arg, value string) error { + if t.o != "" { + return fmt.Errorf("multiple argument: -o %s", value) + } + + t.o = value + return nil + }) + if err := opts.Parse(t.args[1:], func(arg string) error { + if strings.HasPrefix(arg, "-") { + return fmt.Errorf("unexpected option: %s", arg) + } + + switch filepath.Ext(arg) { + case ".h": + t.symSearchOrder = append(t.symSearchOrder, len(t.sources)) + t.sources = append(t.sources, cc.Source{Name: arg}) + case ".c": + t.symSearchOrder = append(t.symSearchOrder, len(t.sources)) + t.sources = append(t.sources, cc.Source{Name: arg, DoNotCache: true}) + case ".json": + t.cdb = arg + return opt.Skip(nil) + default: + return fmt.Errorf("unexpected file type: %s", arg) + } + + return nil + }); err != nil { + switch x := err.(type) { + case opt.Skip: + switch { + case t.compiledbValid: // -compiledb foo.json, create DB + cmd := []string(x)[1:] + if len(cmd) == 0 { + return fmt.Errorf("missing command after -compiledb ") + } + + return t.createCompileDB(cmd) + case t.cdb != "": // foo.json ..., use DB + if err := t.configure(); err != nil { + return err + } + + return t.useCompileDB(t.cdb, x) + } + + return err + default: + return err + } + } + + if t.version { + gobin, err := exec.LookPath("go") + var b []byte + if err == nil { + var bin string + bin, err = exec.LookPath(os.Args[0]) + if err == nil { + b, err = exec.Command(gobin, "version", "-m", bin).CombinedOutput() + } + } + if err == nil { + fmt.Fprintf(t.stdout, "%s", b) + return nil + } + + fmt.Fprintf(t.stdout, "%s\n", Version) + return nil + } + + if t.scriptFn != "" { + return t.scriptBuild(t.scriptFn) + } + + if len(t.sources) == 0 { + return fmt.Errorf("no input files specified") + } + + if t.crtImportPath != "" { + t.l = append(t.l, t.crtImportPath) + t.symSearchOrder = append(t.symSearchOrder, -len(t.l)) + m := map[string]struct{}{} + for _, v := range t.l { + v = strings.TrimSpace(v) + if _, ok := m[v]; !ok { + t.imported = append(t.imported, &imported{path: v}) + m[v] = struct{}{} + } + } + t.imported[len(t.imported)-1].used = true // crt is always imported + } + + if err := t.configure(); err != nil { + return err + } + + abi, err := cc.NewABI(t.goos, t.goarch) + abi.Types[cc.LongDouble] = abi.Types[cc.Double] + if err != nil { + return err + } + + var re *regexp.Regexp + if t.ignoredIncludes != "" { + if re, err = regexp.Compile(t.ignoredIncludes); err != nil { + return err + } + } + + t.cfg.ABI = abi + t.cfg.ReplaceMacroFdZero = t.replaceFdZero + t.cfg.ReplaceMacroTclDefaultDoubleRounding = t.replaceTclDefaultDoubleRounding + t.cfg.ReplaceMacroTclIeeeDoubleRounding = t.replaceTclIeeeDoubleRounding + t.cfg.Config3.IgnoreInclude = re + t.cfg.Config3.NoFieldAndBitfieldOverlap = true + t.cfg.Config3.PreserveWhiteSpace = t.saveConfig == "" + t.cfg.Config3.UnsignedEnums = true + + if t.mingw = detectMingw(t.hostPredefined); t.mingw { + t.windows = true + } + if t.nostdinc { + t.hostIncludes = nil + t.hostSysIncludes = nil + } + var sources []cc.Source + if t.hostPredefined != "" { + sources = append(sources, cc.Source{Name: "", Value: t.hostPredefined}) + } + sources = append(sources, cc.Source{Name: "", Value: builtin}) + if len(t.D) != 0 { + var a []string + for _, v := range t.D { + if i := strings.IndexByte(v, '='); i > 0 { + a = append(a, fmt.Sprintf("#define %s %s", v[:i], v[i+1:])) + continue + } + + a = append(a, fmt.Sprintf("#define %s 1", v)) + } + a = append(a, "\n") + sources = append(sources, cc.Source{Name: "", Value: strings.Join(a, "\n"), DoNotCache: true}) + } + if len(t.U) != 0 { + var a []string + for _, v := range t.U { + a = append(a, fmt.Sprintf("#undef %s", v)) + } + a = append(a, "\n") + sources = append(sources, cc.Source{Name: "", Value: strings.Join(a, "\n"), DoNotCache: true}) + } + + // https://pubs.opengroup.org/onlinepubs/9699919799/utilities/c99.html + // + // Headers whose names are enclosed in double-quotes ( "" ) shall be + // searched for first in the directory of the file with the #include + // line, then in directories named in -I options, and last in the usual + // places + includePaths := append([]string{"@"}, t.I...) + includePaths = append(includePaths, t.hostIncludes...) + includePaths = append(includePaths, t.hostSysIncludes...) + // For headers whose names are enclosed in angle brackets ( "<>" ), the + // header shall be searched for only in directories named in -I options + // and then in the usual places. + sysIncludePaths := append(t.I, t.hostSysIncludes...) + if t.traceTranslationUnits { + fmt.Printf("target: %s/%s\n", t.goos, t.goarch) + if t.hostConfigCmd != "" { + fmt.Printf("host config cmd: %s\n", t.hostConfigCmd) + } + } + for i, v := range t.sources { + tuSources := append(sources, v) + out := t.stdout + if t.saveConfig != "" { + out = io.Discard + t.E = true + } + if t.E { + t.cfg.PreprocessOnly = true + if err := cc.Preprocess(t.cfg, includePaths, sysIncludePaths, tuSources, out); err != nil { + return err + } + memGuard(i, t.isScripted) + continue + } + + var t0 time.Time + if t.traceTranslationUnits { + fmt.Printf("C front end %d/%d: %s ... ", i+1, len(t.sources), v.Name) + t0 = time.Now() + } + ast, err := cc.Translate(t.cfg, includePaths, sysIncludePaths, tuSources) + if err != nil { + return err + } + + if t.traceTranslationUnits { + fmt.Println(time.Since(t0)) + } + t.asts = append(t.asts, ast) + memGuard(i, t.isScripted) + } + if t.E || t.isScripted { + return nil + } + + return t.link() +} + +func (t *Task) configure() (err error) { + if t.configured { + return nil + } + + type jsonConfig struct { + Predefined string + IncludePaths []string + SysIncludePaths []string + OS string + Arch string + } + + t.configured = true + if t.loadConfig != "" { + path := filepath.Join(t.loadConfig, "config.json") + // trc("%p: LOAD_CONFIG(%s)", t, path) + b, err := ioutil.ReadFile(path) + if err != nil { + return err + } + + loadConfig := &jsonConfig{} + if err := json.Unmarshal(b, loadConfig); err != nil { + return err + } + + t.goos = loadConfig.OS + t.goarch = loadConfig.Arch + for _, v := range loadConfig.IncludePaths { + t.hostIncludes = append(t.hostIncludes, filepath.Join(t.loadConfig, v)) + } + for _, v := range loadConfig.SysIncludePaths { + t.hostSysIncludes = append(t.hostSysIncludes, filepath.Join(t.loadConfig, v)) + } + t.hostPredefined = loadConfig.Predefined + return nil + } + + hostConfigOpts := strings.Split(t.hostConfigOpts, ",") + if t.hostConfigOpts == "" { + hostConfigOpts = nil + } + if t.hostPredefined, t.hostIncludes, t.hostSysIncludes, err = cc.HostConfig(t.hostConfigCmd, hostConfigOpts...); err != nil { + return err + } + + if t.saveConfig != "" && !t.configSaved { + t.configSaved = true + // trc("%p: SAVE_CONFIG(%s)", t, t.saveConfig) + cfg := &jsonConfig{ + Predefined: t.hostPredefined, + IncludePaths: t.hostIncludes, + SysIncludePaths: t.hostSysIncludes, + OS: t.goos, + Arch: t.goarch, + } + b, err := json.Marshal(cfg) + if err != nil { + return err + } + + full := filepath.Join(t.saveConfig, "config.json") + if err := os.MkdirAll(t.saveConfig, 0700); err != nil { + return err + } + + if err := ioutil.WriteFile(full, b, 0600); err != nil { + return err + } + } + + return nil +} + +func (t *Task) setLookPaths() (err error) { + if t.ccLookPath, err = exec.LookPath(t.cc); err != nil { + return err + } + + t.arLookPath, err = exec.LookPath(t.ar) + return err +} + +func (t *Task) link() (err error) { + if len(t.asts) == 0 { + return fmt.Errorf("no objects to link") + } + + if t.o == "" { + t.o = fmt.Sprintf("a_%s_%s.go", t.goos, t.goarch) + } + dir := filepath.Dir(t.o) + t.capif = filepath.Join(dir, fmt.Sprintf("capi_%s_%s.go", t.goos, t.goarch)) + f, err2 := os.Create(t.o) + if err2 != nil { + return err2 + } + + defer func() { + if e := f.Close(); e != nil && err == nil { + err = e + return + } + + if out, e := exec.Command("gofmt", "-r", "(x) -> x", "-l", "-s", "-w", t.o).CombinedOutput(); e != nil && err == nil { + err = fmt.Errorf(strings.Join([]string{string(out), e.Error()}, ": ")) + } + if out, e := exec.Command("gofmt", "-l", "-s", "-w", t.o).CombinedOutput(); e != nil && err == nil { + err = fmt.Errorf(strings.Join([]string{string(out), e.Error()}, ": ")) + } + }() + + w := bufio.NewWriter(f) + + defer func() { + if e := w.Flush(); e != nil && err == nil { + err = e + } + }() + + t.out = w + p, err := newProject(t) + if err != nil { + return err + } + + return p.main() +} + +func (t *Task) scriptBuild(fn string) error { + f, err := os.Open(fn) + if err != nil { + return err + } + + defer f.Close() + + r := csv.NewReader(f) + r.Comment = '#' + r.FieldsPerRecord = -1 + r.TrimLeadingSpace = true + script, err := r.ReadAll() + if err != nil { + return err + } + + return t.scriptBuild2(script) +} + +func (t *Task) scriptBuild2(script [][]string) error { + var ldir string + ccgo := []string{t.args[0]} + for i, line := range script { + dir := line[0] + args := line[1:] + for _, v := range args { + if strings.HasSuffix(v, ".c") || strings.HasSuffix(v, ".h") { + v = filepath.Join(dir, v) + t.symSearchOrder = append(t.symSearchOrder, len(t.sources)) + t.sources = append(t.sources, cc.Source{Name: v}) + } + } + cmd := append(ccgo, args...) + if t.traceTranslationUnits { + if dir != ldir { + fmt.Println(dir) + ldir = dir + } + fmt.Printf("%s\n", cmd) + } + t2 := NewTask(append(ccgo, args...), t.stdout, t.stderr) + t2.cfg.IncludeFileHandler = t.cfg.IncludeFileHandler + t2.cfg.SharedFunctionDefinitions = t.cfg.SharedFunctionDefinitions + t2.configSaved = t.configSaved + t2.configured = t.configured + t2.hostIncludes = t.hostIncludes + t2.hostPredefined = t.hostPredefined + t2.hostSysIncludes = t.hostSysIncludes + t2.includedFiles = t.includedFiles + t2.isScripted = true + t2.loadConfig = t.loadConfig + t2.replaceFdZero = t.replaceFdZero + t2.replaceTclDefaultDoubleRounding = t.replaceTclDefaultDoubleRounding + t2.replaceTclIeeeDoubleRounding = t.replaceTclIeeeDoubleRounding + t2.saveConfig = t.saveConfig + if err := inDir(dir, t2.Main); err != nil { + return err + } + + t.asts = append(t.asts, t2.asts...) + if i == 0 { + t.cfg = t2.cfg + } + } + if t.crtImportPath != "" { + t.l = append(t.l, t.crtImportPath) + t.symSearchOrder = append(t.symSearchOrder, -len(t.l)) + m := map[string]struct{}{} + for _, v := range t.l { + v = strings.TrimSpace(v) + if _, ok := m[v]; !ok { + t.imported = append(t.imported, &imported{path: v}) + m[v] = struct{}{} + } + } + t.imported[len(t.imported)-1].used = true // crt is always imported + } + if t.saveConfig != "" { + return nil + } + + return t.link() +} + +type cdb struct { + items []*cdbItem + outputIndex map[string][]*cdbItem +} + +func (db *cdb) find(obj map[string]*cdbItem, nm string, ver, seqLimit int, path []string, cc, ar string, ignored map[string]struct{}) error { + // trc("%v: nm %q ver %v seqLimit %v path %q cc %q ar %q", origin(1), nm, ver, seqLimit, path, cc, ar) + var item *cdbItem + var k string + switch { + case ver < 0: + // find highest ver with .seq < seqLimit + for i, v := range db.outputIndex[nm] { + if v.seq >= seqLimit { + break + } + + item = v + ver = i + } + if item == nil { + ver = -1 + for _, v := range db.items { + if seqLimit >= 0 && v.seq >= seqLimit { + break + } + + if filepath.Base(v.Output) == filepath.Base(nm) { + item = v + ver = v.ver + break + } + } + } + + k = fmt.Sprintf("%s#%d", nm, ver) + default: + // match ver exactly + k = fmt.Sprintf("%s#%d", nm, ver) + if obj[k] != nil { + return nil + } + + items := db.outputIndex[nm] + switch { + case ver < len(items): + panic(todo("", nm, ver, seqLimit)) + default: + n := -1 + for _, v := range db.items { + if seqLimit >= 0 && v.seq >= seqLimit { + break + } + + if filepath.Base(v.Output) == filepath.Base(nm) { + n++ + if n == ver { + item = v + break + } + } + } + } + } + if item == nil { + for k := range ignored { + if k == nm || strings.HasSuffix(nm, k) { + return nil + } + } + + return fmt.Errorf("not found in compile DB: %s (max seq %d), path %v", k, seqLimit, path) + } + + if obj[k] != nil { + return nil + } + + obj[k] = item + var errs []string + for _, v := range item.sources(cc, ar) { + if err := db.find(obj, v, -1, item.seq, append(path, nm), cc, ar, ignored); err != nil { + errs = append(errs, err.Error()) + } + } + if len(errs) != 0 { + sort.Strings(errs) + w := 0 + for _, v := range errs { + if w == 0 || w > 0 && v != errs[w-1] { + errs[w] = v + w++ + } + } + errs = errs[:w] + return fmt.Errorf("%s", strings.Join(errs, "\n")) + } + + return nil +} + +func suffixNum(s string, dflt int) (string, int) { + x := strings.LastIndexByte(s, '#') + if x < 0 { + return s, dflt + } + + // foo#42 + // 012345 + // x == 3 + num := s[x+1:] + n, err := strconv.ParseUint(num, 10, 32) + if err != nil { + return s, dflt + } + + return s[:x], int(n) +} + +func (t *Task) useCompileDB(fn string, args []string) error { + if err := t.setLookPaths(); err != nil { + return err + } + + var cdb cdb + f, err := os.Open(fn) + if err != nil { + return err + } + + de := json.NewDecoder(f) + err = de.Decode(&cdb.items) + f.Close() + if err != nil { + return err + } + + cdb.outputIndex = map[string][]*cdbItem{} + for i, v := range cdb.items { + v.seq = i + if len(v.Arguments) == 0 { + if len(v.Command) == 0 { + return fmt.Errorf("either arguments or command is required: %+v", v) + } + + if v.Arguments, err = shellquote.Split(v.Command); err != nil { + return err + } + } + + k := v.output(t.ccLookPath, t.arLookPath) + a := cdb.outputIndex[k] + v.ver = len(a) + cdb.outputIndex[k] = append(a, v) + } + obj := map[string]*cdbItem{} + notFound := false + for _, v := range args { + v, ver := suffixNum(v, 0) + if err := cdb.find(obj, v, ver, -1, nil, t.ccLookPath, t.arLookPath, t.ignoredObjects); err != nil { + notFound = true + fmt.Fprintln(os.Stderr, err) + } + } + if notFound { + var a []string + for k, v := range cdb.outputIndex { + for _, w := range v { + a = append(a, fmt.Sprintf("%5d %s", w.seq, k)) + } + } + sort.Strings(a) + fmt.Fprintf(os.Stderr, "compile DB index:\n\t%s\n", strings.Join(a, "\n\t")) + } + + var a []string + for k := range obj { + a = append(a, k) + } + sort.Strings(a) + return t.cdbBuild(obj, a) +} + +func (t *Task) cdbBuild(obj map[string]*cdbItem, list []string) error { + var script [][]string + for _, nm := range list { + it := obj[nm] + if !strings.HasSuffix(it.Output, ".o") || it.Arguments[0] != t.cc { + continue + } + + args, err := it.ccgoArgs(t.cc) + if err != nil { + return err + } + + for _, v := range t.D { + args = append(args, "-D"+v) + } + for _, v := range t.U { + args = append(args, "-U"+v) + } + + line := append([]string{it.Directory}, args...) + script = append(script, line) + } + return t.scriptBuild2(script) +} + +func (t *Task) createCompileDB(command []string) (rerr error) { + if err := t.setLookPaths(); err != nil { + return err + } + + cwd, err := os.Getwd() + if err != nil { + return err + } + + f, err := os.Create(t.compiledb) + if err != nil { + return err + } + + defer func() { + if err := f.Close(); err != nil && rerr == nil { + rerr = err + } + }() + + cwr := newCDBWriter(f) + + defer func() { + if err := cwr.finish(); err != nil && rerr == nil { + rerr = err + } + }() + + var cmd *exec.Cmd + var parser func(s string) ([]string, error) +out: + switch t.goos { + case "darwin", "freebsd", "netbsd", "openbsd": + switch command[0] { + case "make", "gmake": + // ok + default: + return fmt.Errorf("usupported build command: %s", command[0]) + } + + sh, err := exec.LookPath("sh") + if err != nil { + return err + } + + command = append([]string{sh, "-c"}, join(" ", command[0], "SHELL='sh -x'", command[1:])) + cmd = exec.Command(command[0], command[1:]...) + parser = makeXParser + case "windows": + if command[0] != "make" { + return fmt.Errorf("usupported build command: %s", command[0]) + } + + switch s := runtime.GOOS; s { + case "windows": + argv := append([]string{"-d"}, command[1:]...) + command[0] += ".exe" + cmd = exec.Command(command[0], argv...) + parser = makeDParser + break out + case "linux": + // ok + default: + return fmt.Errorf("usupported cross compile host: %s", s) + } + + fallthrough + default: + strace, err := exec.LookPath("strace") + if err != nil { + return err + } + + argv := append([]string{"-f", "-s1000000", "-e", "trace=execve"}, command...) + cmd = exec.Command(strace, argv...) + parser = straceParser + } + cmd.Env = append(os.Environ(), "LC_ALL=C") + cw := t.newCdbMakeWriter(cwr, cwd, parser) + switch { + case t.verboseCompiledb: + cmd.Stdout = io.MultiWriter(cw, os.Stdout) + default: + cmd.Stdout = cw + } + cmd.Stderr = cmd.Stdout + if dmesgs { + dmesg("%v: %v", origin(1), cmd.Args) + } + if err := cmd.Run(); err != nil { + if dmesgs { + dmesg("%v: cmd.Run: %v", origin(1), err) + } + return err + } + + return cw.err +} + +func makeDParser(s string) ([]string, error) { + const prefix = "CreateProcess(" + if !strings.HasPrefix(s, prefix) { + return nil, nil + } + + // s: `CreateProcess(C:\Program Files\CodeBlocks\MinGW\bin\gcc.exe,gcc -O3 -Wall -c -o compress.o compress.c,...)` + s = s[len(prefix):] + // s: `C:\Program Files\CodeBlocks\MinGW\bin\gcc.exe,gcc -O3 -Wall -c -o compress.o compress.c,...)` + x := strings.IndexByte(s, ',') + if x < 0 { + return nil, nil + } + + cmd := s[:x] + // cmd: `C:\Program Files\CodeBlocks\MinGW\bin\gcc.exe` + + s = s[x+1:] + // s: `gcc -O3 -Wall -c -o compress.o compress.c,...)` + if x = strings.LastIndexByte(s, ','); x < 0 { + return nil, nil + } + + s = s[:x] + // s: `gcc -O3 -Wall -c -o compress.o compress.c` + a, err := shellquote.Split(strings.TrimSpace(s)) + if err != nil || len(a) == 0 { + return nil, err + } + + return append([]string{cmd}, a[1:]...), nil +} + +func isCreateArchive(s string) bool { + // ar modifiers may be in any order so sort characters in s before checking. + // This turns eg `rc` into `cr`. + b := []byte(s) + sort.Slice(b, func(i, j int) bool { return b[i] < b[j] }) + switch string(b) { + case "cq", "cr", "crs", "cru": + return true + } + return false +} + +func hasPlusPrefix(s string) (n int, r string) { + for strings.HasPrefix(s, "+") { + n++ + s = s[1:] + } + return n, s +} + +func makeXParser(s string) (r []string, err error) { + switch { + case strings.HasPrefix(s, "libtool: link: ar "): + s = s[len("libtool: link:"):] + case strings.HasPrefix(s, "libtool: compile: "): + s = s[len("libtool: compile:"):] + for strings.HasPrefix(s, " ") { + s = s[1:] + } + default: + var n int + if n, s = hasPlusPrefix(s); n == 0 { + return nil, nil + } + } + + if !strings.HasPrefix(s, " ") { + return nil, nil + } + + s = s[1:] + r, err = shellquote.Split(s) + if err != nil { + if strings.Contains(err.Error(), "Unterminated single-quoted string") { + return nil, nil // ignore + } + } + if len(r) != 0 && filepath.Base(r[0]) == "libtool" { + r[0] = "libtool" + } + return r, err +} + +func straceParser(s string) ([]string, error) { + prefix := "execve(" + if strings.HasPrefix(s, "[pid ") { + s = strings.TrimSpace(s[strings.IndexByte(s, ']')+1:]) + } + if !strings.HasPrefix(s, prefix) || !strings.HasSuffix(s, ") = 0") { + return nil, nil + } + + // s: `execve("/usr/bin/ar", ["ar", "cr", "libtcl8.6.a", "regcomp.o", ... "bn_s_mp_sqr.o", "bn_s_mp_sub.o"], 0x55e6bbf49648 /* 60 vars */) = 0` + s = s[len(prefix):] + // s: `"/usr/bin/ar", ["ar", "cr", "libtcl8.6.a", "regcomp.o", ... "bn_s_mp_sqr.o", "bn_s_mp_sub.o"], 0x55e6bbf49648 /* 60 vars */) = 0` + a := strings.SplitN(s, ", [", 2) + // a[0]: `"/usr/bin/ar"`, a[1]: `"ar", "cr", "libtcl8.6.a", "regcomp.o", ... "bn_s_mp_sqr.o", "bn_s_mp_sub.o"], 0x55e6bbf49648 /* 60 vars */) = 0` + args := a[1] + // args: `"ar", "cr", "libtcl8.6.a", "regcomp.o", ... "bn_s_mp_sqr.o", "bn_s_mp_sub.o"], 0x55e6bbf49648 /* 60 vars */) = 0` + args = args[:strings.LastIndex(args, "], ")] + // args: `"ar", "cr", "libtcl8.6.a", "regcomp.o", ... "bn_s_mp_sqr.o", "bn_s_mp_sub.o"` + argv, err := shellquote.Split(args) + if err != nil { + return nil, err + } + + words, err := shellquote.Split(a[0]) + if err != nil { + return nil, err + } + + argv[0] = words[0] + for i, v := range argv { + if strings.HasSuffix(v, ",") { + v = v[:len(v)-1] + } + if v2, err := strconv.Unquote(`"` + v + `"`); err == nil { + v = v2 + } + argv[i] = v + } + + return argv, nil +} + +type cdbItem struct { + Arguments []string `json:"arguments"` + Command string `json:"command,omitempty"` + Directory string `json:"directory"` + File string `json:"file"` + Output string `json:"output,omitempty"` + + seq int + ver int +} + +func (it *cdbItem) cmpString() string { return fmt.Sprint(*it) } + +func (it *cdbItem) ccgoArgs(cc string) (r []string, err error) { + switch it.Arguments[0] { + case cc: + set := opt.NewSet() + set.Arg("D", true, func(opt, arg string) error { r = append(r, "-D"+arg); return nil }) + set.Arg("I", true, func(opt, arg string) error { r = append(r, "-I"+arg); return nil }) + set.Arg("MF", true, func(opt, arg string) error { return nil }) + set.Arg("MT", true, func(opt, arg string) error { return nil }) + set.Arg("O", true, func(opt, arg string) error { return nil }) + set.Arg("U", true, func(opt, arg string) error { r = append(r, "-U"+arg); return nil }) + set.Arg("o", true, func(opt, arg string) error { return nil }) + set.Arg("std", true, func(opt, arg string) error { return nil }) + set.Opt("MD", func(opt string) error { return nil }) + set.Opt("MMD", func(opt string) error { return nil }) + set.Opt("MP", func(opt string) error { return nil }) + set.Opt("ansi", func(opt string) error { return nil }) + set.Opt("c", func(opt string) error { return nil }) + set.Opt("g", func(opt string) error { return nil }) + set.Opt("pedantic", func(opt string) error { return nil }) + set.Opt("pipe", func(opt string) error { return nil }) + set.Opt("pthread", func(opt string) error { return nil }) + set.Opt("s", func(opt string) error { return nil }) + set.Opt("w", func(opt string) error { return nil }) + if err := set.Parse(it.Arguments[1:], func(arg string) error { + switch { + case strings.HasSuffix(arg, ".c"): + r = append(r, arg) + case + + strings.HasPrefix(arg, "-W"), + strings.HasPrefix(arg, "-f"), + strings.HasPrefix(arg, "-m"): + + // nop + case strings.HasPrefix(arg, ">"): + return opt.Skip(nil) + default: + return fmt.Errorf("unknown/unsupported CC option: %s", arg) + } + + return nil + }); err != nil { + switch err.(type) { + case opt.Skip: + // ok + default: + return nil, err + } + } + + return r, nil + default: + return nil, fmt.Errorf("command not supported: %q", it.Arguments[0]) + } +} + +func (it *cdbItem) output(cc, ar string) (r string) { + if it.Output != "" { + return it.Output + } + + if len(it.Arguments) == 0 { + return "" + } + + switch it.Arguments[0] { + case cc: + for i, v := range it.Arguments { + if v == "-o" && i < len(it.Arguments)-1 { + it.Output = filepath.Join(it.Directory, it.Arguments[i+1]) + break + } + } + if it.Output == "" && strings.HasSuffix(it.File, ".c") { + for _, v := range it.Arguments { + if v == "-c" { + bn := filepath.Base(it.File) + it.Output = filepath.Join(it.Directory, bn[:len(bn)-2]+".o") + break + } + } + } + case ar: + if isCreateArchive(it.Arguments[1]) { + it.Output = filepath.Join(it.Directory, it.Arguments[2]) + } + case "libtool": + for i, v := range it.Arguments { + if v == "-o" && i < len(it.Arguments)-1 { + it.Output = filepath.Join(it.Directory, it.Arguments[i+1]) + } + } + } + return it.Output +} + +func (it *cdbItem) sources(cc, ar string) (r []string) { + if len(it.Arguments) == 0 { + return nil + } + + switch arg0 := it.Arguments[0]; arg0 { + case + "libtool", + ar, + filepath.Base(ar), + cc: + + var prev string + for _, v := range it.Arguments { + switch prev { + case "-o", "-MT", "-MF": + // nop + default: + if strings.HasSuffix(v, ".o") { + r = append(r, filepath.Join(it.Directory, v)) + } + } + prev = v + } + return r + default: + panic(todo("cc: %q ar: %q it: %+v", cc, ar, it)) + } +} + +type cdbMakeWriter struct { + b bytes.Buffer + cc string + ar string + arBase string + dir string + err error + it cdbItem + parser func(s string) ([]string, error) + sc *bufio.Scanner + w *cdbWriter +} + +func (t *Task) newCdbMakeWriter(w *cdbWriter, dir string, parser func(s string) ([]string, error)) *cdbMakeWriter { + const sz = 1 << 16 + r := &cdbMakeWriter{ + cc: t.ccLookPath, + ar: t.arLookPath, + arBase: filepath.Base(t.arLookPath), + dir: dir, + parser: parser, + w: w, + } + r.sc = bufio.NewScanner(&r.b) + r.sc.Buffer(make([]byte, sz), sz) + return r +} + +func (w *cdbMakeWriter) fail(err error) { + if w.err == nil { + w.err = fmt.Errorf("%v (%v)", err, origin(2)) + } +} + +func (w *cdbMakeWriter) Write(b []byte) (int, error) { + w.b.Write(b) + for bytes.Contains(w.b.Bytes(), []byte{'\n'}) { + if !w.sc.Scan() { + panic(todo("internal error")) + } + + s := strings.TrimSpace(w.sc.Text()) + if edx := strings.Index(s, "Entering directory"); edx >= 0 { + s = s[edx+len("Entering directory"):] + s = strings.TrimSpace(s) + if len(s) == 0 { + continue + } + + if (s[0] == '\'' || s[0] == '`') && s[len(s)-1] == '\'' { + s = s[1:] + if len(s) == 0 { + continue + } + + s = s[:len(s)-1] + } + s = `"` + s + `"` + dir, err := strconv.Unquote(s) + if err != nil { + w.fail(err) + continue + } + + dir = filepath.Clean(dir) + if dir == w.dir { + continue + } + + w.dir = dir + fmt.Printf("cd %s\n", dir) + continue + } + + args, err := w.parser(s) + if err != nil { + w.fail(err) + continue + } + + if len(args) == 0 { + continue + } + + // TODO: change so eg handleGCC returns []cdbItem, skip if none. + + w.it = cdbItem{} + + err = nil + switch args[0] { + case w.cc: + fmt.Printf("CCGO CC: %q\n", args) + err = w.handleGCC(args) + case w.ar: + fallthrough + case w.arBase: + if isCreateArchive(args[1]) { + fmt.Printf("CCGO AR: %q\n", args) + err = w.handleAR(args) + } + case "libtool": + fmt.Printf("CCGO LIBTOOL: %q\n", args) + err = w.handleLibtool(args) + } + if err != nil { + w.fail(err) + continue + } + + if w.it.Output != "" { + w.w.add(w.it) + } + } + return len(b), nil +} + +func (w *cdbMakeWriter) handleLibtool(args []string) error { + w.it = cdbItem{ + Arguments: args, + Directory: w.dir, + } + for i, v := range args { + switch { + case v == "-o" && i < len(args)-1: + w.it.Output = filepath.Join(w.dir, args[i+1]) + } + } + w.it.output(w.cc, w.ar) + return nil +} + +func (w *cdbMakeWriter) handleAR(args []string) error { + w.it = cdbItem{ + Arguments: args, + Directory: w.dir, + } + // TODO: assumes isCreateArchive has already been checked + w.it.Output = filepath.Join(w.dir, args[2]) + return nil +} + +func (w *cdbMakeWriter) handleGCC(args []string) error { + w.it = cdbItem{ + Arguments: args, + Directory: w.dir, + } + for i, v := range args { + switch { + case v == "-o" && i < len(args)-1: + w.it.Output = filepath.Join(w.dir, args[i+1]) + case strings.HasSuffix(v, ".c"): + if w.it.File != "" { + return fmt.Errorf("multiple .c files: %s", v) + } + + w.it.File = filepath.Clean(v) + } + } + w.it.output(w.cc, w.ar) + return nil +} + +type cdbWriter struct { + w *bufio.Writer + items []cdbItem +} + +func newCDBWriter(w io.Writer) *cdbWriter { + return &cdbWriter{w: bufio.NewWriter(w)} +} + +func (w *cdbWriter) add(item cdbItem) { + w.items = append(w.items, item) +} + +func (w *cdbWriter) finish() error { + enc := json.NewEncoder(w.w) + enc.SetIndent("", " ") + if err := enc.Encode(w.items); err != nil { + return err + } + return w.w.Flush() +} + +func join(sep string, a ...interface{}) string { + var b []string + for _, v := range a { + switch x := v.(type) { + case string: + b = append(b, x) + case []string: + b = append(b, x...) + default: + panic(todo("internal error: %T", x)) + } + } + return strings.Join(b, sep) +} + +func inDir(dir string, f func() error) (err error) { + var cwd string + if cwd, err = os.Getwd(); err != nil { + return err + } + + defer func() { + if err2 := os.Chdir(cwd); err2 != nil { + err = err2 + } + }() + + if err = os.Chdir(dir); err != nil { + return err + } + + return f() +} + +func detectMingw(s string) bool { + return strings.Contains(s, "#define __MINGW") +} + +func memGuard(i int, force bool) { + if totalRam == 0 || totalRam > 64e9 { + return + } + + var ms runtime.MemStats + runtime.ReadMemStats(&ms) + switch { + case ms.Alloc < totalRam/2: + return + case ms.Alloc < (8*totalRam)/10: + if force { + break + } + + switch { + case totalRam < 1e9: + // ok + case totalRam < 16e9: + if i&1 == 1 { + return + } + default: + if i&3 != 3 { + return + } + } + } + + debug.FreeOSMemory() +} diff --git a/vendor/modernc.org/ccgo/v3/lib/cover.go b/vendor/modernc.org/ccgo/v3/lib/cover.go new file mode 100644 index 00000000..fdc39f3f --- /dev/null +++ b/vendor/modernc.org/ccgo/v3/lib/cover.go @@ -0,0 +1,39 @@ +// Copyright 2020 The CCGO 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 ccgo // import "modernc.org/ccgo/v3/lib" + +import ( + "fmt" + "runtime" + "sort" + "strings" +) + +var ( + coverMap = map[uintptr]struct{}{} +) + +func pc2origin(pc uintptr) string { + f := runtime.FuncForPC(pc) + var fn, fns string + var fl int + if f != nil { + fn, fl = f.FileLine(pc) + fns = f.Name() + if x := strings.LastIndex(fns, "."); x > 0 { + fns = fns[x+1:] + } + } + return fmt.Sprintf("%s:%d:%s", fn, fl, fns) +} + +func coverReport() string { + var a []string + for pc := range coverMap { + a = append(a, pc2origin(pc)) + } + sort.Strings(a) + return strings.Join(a, "\n") +} diff --git a/vendor/modernc.org/ccgo/v3/lib/design-notes.adoc b/vendor/modernc.org/ccgo/v3/lib/design-notes.adoc new file mode 100644 index 00000000..880ce7ee --- /dev/null +++ b/vendor/modernc.org/ccgo/v3/lib/design-notes.adoc @@ -0,0 +1,91 @@ += Design Notes + +== Problems: + +Translating C to Go is harder than it looks. + +Jan says: It's impossible in the general case to turn C char* into Go +[]byte. It's possible to do it probably often for concrete C code +cases - based also on author's C coding style. The first problem this +runs into is that Go does not guarantee that the backing array will +keep its address stable due to Go movable stacks. C expects the +opposite, a pointer never magically modifies itself, so some code will +fail. + +INSERT CODE EXAMPLES ILLUSTRATING THE PROBLEM HERE + +== How the parser works + +There are no comment nodes in the C AST. Instead every cc.Token has a +Sep field: https://godoc.org/modernc.org/cc/v3#Token + +It captures, when configured to do so, all white space preceding the +token, combined, including comments, if any. So we have all white +space/comments information for every token in the AST. A final white +space/comment, preceding EOF, is available as field TrailingSeperator +in the AST: https://godoc.org/modernc.org/cc/v3#AST. + +To get the lexically first white space/comment for any node, use +tokenSeparator(): +https://gitlab.com/cznic/ccgo/-/blob/6551e2544a758fdc265c8fac71fb2587fb3e1042/v3/go.go#L1476 + +The same with a default value is comment(): +https://gitlab.com/cznic/ccgo/-/blob/6551e2544a758fdc265c8fac71fb2587fb3e1042/v3/go.go#L1467 + +== Looking forward + +Eric says: In my visualization of how the translator would work, the +output of a ccgo translation of a module at any given time is a file +of pseudo-Go code in which some sections may be enclosed by a Unicode +bracketing character (presently using the guillemot quotes U+ab and +U+bb) meaning "this is not Go yet" that intentionally makes the Go +compiler barf. This expresses a color on the AST nodes. + +So, for example, if I'm translating hello.c with a ruleset that does not +include print -> fmt.Printf, this: + +--------------------------------------------------------- +#include + +/* an example comment */ + +int main(int argc, char *argv[]) +{ + printf("Hello, World") +} +--------------------------------------------------------- + +becomes this without any explicit rules at all: + +--------------------------------------------------------- +«#include » + +/* an example comment */ + +func main +{ + «printf(»"Hello, World"!\n"«)» +} +--------------------------------------------------------- + +Then, when the rule print -> fmt.Printf is added, it becomes + +--------------------------------------------------------- +import ( + "fmt" +) + +/* an example comment */ + +func main +{ + fmt.Printf("Hello, World"!\n") +} +--------------------------------------------------------- + +because with that rule the AST node corresponding to the printf +call can be translated and colored "Go". This implies an import +of fmt. We observe that there are no longer C-colored spans +and drop the #includes. + +// end diff --git a/vendor/modernc.org/ccgo/v3/lib/dmesg.go b/vendor/modernc.org/ccgo/v3/lib/dmesg.go new file mode 100644 index 00000000..c7da87ee --- /dev/null +++ b/vendor/modernc.org/ccgo/v3/lib/dmesg.go @@ -0,0 +1,42 @@ +// Copyright 2021 The CCGO Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ccgo.dmesg +// +build ccgo.dmesg + +package ccgo // import "modernc.org/ccgo/v3/lib" + +import ( + "fmt" + "os" + "path/filepath" + "strings" +) + +const dmesgs = true + +var ( + pid = fmt.Sprintf("[%v %v] ", os.Getpid(), filepath.Base(os.Args[0])) + logf *os.File +) + +func init() { + var err error + if logf, err = os.OpenFile("/tmp/ccgo.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY|os.O_SYNC, 0644); err != nil { + panic(err.Error()) + } +} + +func dmesg(s string, args ...interface{}) { + if s == "" { + s = strings.Repeat("%v ", len(args)) + } + s = fmt.Sprintf(pid+s, args...) + switch { + case len(s) != 0 && s[len(s)-1] == '\n': + fmt.Fprint(logf, s) + default: + fmt.Fprintln(logf, s) + } +} diff --git a/vendor/modernc.org/ccgo/v3/lib/etc.go b/vendor/modernc.org/ccgo/v3/lib/etc.go new file mode 100644 index 00000000..e3b8c577 --- /dev/null +++ b/vendor/modernc.org/ccgo/v3/lib/etc.go @@ -0,0 +1,110 @@ +// Copyright 2020 The CCGO 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 ccgo // import "modernc.org/ccgo/v3/lib" + +import ( + "fmt" + "math" + "math/big" + + "modernc.org/cc/v3" +) + +var ( + reservedNames = map[string]bool{ + "bool": false, // ccgo can use + "break": true, // keyword + "case": true, // keyword + "chan": true, // keyword + "const": true, // keyword + "continue": true, // keyword + "default": true, // keyword + "defer": true, // keyword + "else": true, // keyword + "fallthrough": true, // keyword + "false": false, // ccgo can use + "float32": false, // ccgo can use + "float64": false, // ccgo can use + "for": true, // keyword + "func": true, // keyword + "go": true, // keyword + "goto": true, // keyword + "if": true, // keyword + "import": true, // keyword + "init": false, // special name + "int16": false, // ccgo can use + "int32": false, // ccgo can use + "int64": false, // ccgo can use + "int8": false, // ccgo can use + "interface": true, // keyword + "map": true, // keyword + "math": false, // package name + "nil": false, // ccgo can use + "package": true, // keyword + "range": true, // keyword + "return": true, // keyword + "select": true, // keyword + "struct": true, // keyword + "switch": true, // keyword + "true": false, // ccgo can use + "type": true, // keyword + "types": false, // package name + "uint16": false, // ccgo can use + "uint32": false, // ccgo can use + "uint64": false, // ccgo can use + "uint8": false, // ccgo can use + "uintptr": false, // ccgo can use + "unsafe": false, // package name + "var": true, // keyword + } + + reservedIds []cc.StringID + + maxInt32 = big.NewInt(math.MaxInt32) + maxInt64 = big.NewInt(math.MaxInt64) + maxUint32 = big.NewInt(math.MaxUint32) + maxUint64 = big.NewInt(0).SetUint64(math.MaxUint64) + minInt32 = big.NewInt(math.MinInt32) + minInt64 = big.NewInt(math.MinInt64) +) + +func init() { + for k := range reservedNames { + reservedIds = append(reservedIds, cc.String(k)) + } +} + +type scope map[cc.StringID]int32 + +func newScope() scope { + s := scope{} + for _, k := range reservedIds { + s[k] = 0 + } + return s +} + +func (s scope) take(t cc.StringID) string { + if t == 0 { + panic(todo("internal error")) + } + + n, ok := s[t] + if !ok { + s[t] = 0 + return t.String() + } + + for { + n++ + s[t] = n + r := fmt.Sprintf("%s%d", t, n) + id := cc.String(r) + if _, ok := s[id]; !ok { + s[id] = 0 + return r + } + } +} diff --git a/vendor/modernc.org/ccgo/v3/lib/go.go b/vendor/modernc.org/ccgo/v3/lib/go.go new file mode 100644 index 00000000..8e6bccc7 --- /dev/null +++ b/vendor/modernc.org/ccgo/v3/lib/go.go @@ -0,0 +1,13056 @@ +// Copyright 2020 The CCGO 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 ccgo // import "modernc.org/ccgo/v3/lib" + +import ( + "bytes" + "fmt" + "go/scanner" + "go/token" + "hash/maphash" + "io/ioutil" + "math" + "math/big" + "os" + "os/exec" + "path/filepath" + "runtime" + "sort" + "strconv" + "strings" + "sync" + "time" + + "modernc.org/cc/v3" + "modernc.org/mathutil" +) + +var ( + idAddOverflow = cc.String("__builtin_add_overflow") // bool __builtin_add_overflow (type1 a, type2 b, type3 *res) + idAlias = cc.String("alias") + idAligned = cc.String("aligned") // int __attribute__ ((aligned (8))) foo; + idAtomicLoadN = cc.String("__atomic_load_n") // type __atomic_load_n (type *ptr, int memorder) + idAtomicStoreN = cc.String("__atomic_store_n") // void __atomic_store_n (type *ptr, type val, int memorder) + idBp = cc.String("bp") + idBuiltinConstantPImpl = cc.String("__builtin_constant_p_impl") + idCAPI = cc.String("CAPI") + idChooseExpr = cc.String("__builtin_choose_expr") + idEnviron = cc.String("environ") + idMain = cc.String("main") + idMulOverflow = cc.String("__builtin_mul_overflow") // bool __builtin_mul_overflow (type1 a, type2 b, type3 *res) + idPacked = cc.String("packed") // __attribute__((packed)) + idSubOverflow = cc.String("__builtin_sub_overflow") // bool __builtin_sub_overflow (type1 a, type2 b, type3 *res) + idTls = cc.String("tls") + idTransparentUnion = cc.String("__transparent_union__") + idTs = cc.String("ts") + idVa = cc.String("va") + idVaArg = cc.String("__ccgo_va_arg") + idVaEnd = cc.String("__ccgo_va_end") + idVaList = cc.String("va_list") + idVaStart = cc.String("__ccgo_va_start") + idWcharT = cc.String("wchar_t") + idWinWchar = cc.String("WCHAR") + idWtext = cc.String("wtext") + + bytesBufferPool = sync.Pool{New: func() interface{} { return &bytes.Buffer{} }} + + oTraceG bool + oTraceW bool + oTracePin bool +) + +type exprMode int + +const ( + doNotExport = iota + doNotChange + exportCapitalize + exportPrefix +) + +const ( + _ exprMode = iota + exprAddrOf // &foo as uinptr (must be static/pinned) + exprBool // foo in foo != 0 + exprCondInit // foo or bar in int i = x ? foo : bar; + exprCondReturn // foo or bar in return x ? foo : bar; + exprDecay // &foo[0] in foo for array foo. + exprFunc // foo in foo(bar) + exprLValue // foo in foo = bar + exprPSelect // foo in foo->bar + exprSelect // foo in foo.bar + exprValue // foo in bar = foo + exprVoid // + exprGoPtr +) + +const ( + tooManyErrors = "too many errors" +) + +type opKind int + +const ( + opNormal opKind = iota + opArray + opArrayParameter + opFunction + opUnion + opBitfield + opStruct +) + +type flags byte + +const ( + fForceConv flags = 1 << iota + fForceNoConv + fForceRuntimeConv + fNoCondAssignment + fAddrOfFuncPtrOk +) + +type imported struct { + path string // Eg. "example.com/user/foo". + name string // Eg. "foo" from "package foo". + qualifier string // Eg. "foo." or "foo2." if renamed due to name conflict. + exports map[string]struct{} // Eg. {"New": {}, "Close": {}, ...}. + + used bool +} + +type taggedStruct struct { + ctyp cc.Type + gotyp string + name string + node cc.Node + + conflicts bool + emitted bool +} + +func (s *taggedStruct) emit(p *project, ds *cc.DeclarationSpecifiers) { + if s == nil || s.emitted { + return + } + + s.emitted = true + p.w("%stype %s = %s; /* %v */\n\n", tidyComment("\n", ds), s.name, s.gotyp, p.pos(s.node)) +} + +// Return first non empty token separator within n or dflt otherwise. +func comment(dflt string, n cc.Node) string { + if s := tokenSeparator(n); s != "" { + return s + } + + return dflt +} + +// tidyComment is like comment but makes comment more Go-like. +func tidyComment(dflt string, n cc.Node) (r string) { return tidyCommentString(comment(dflt, n)) } + +func tidyCommentString(s string) (r string) { + defer func() { + if !strings.Contains(r, "//
") {
+			return
+		}
+
+		a := strings.Split(r, "\n")
+		in := false
+		for i, v := range a {
+			switch {
+			case in:
+				if strings.HasPrefix(v, "// 
") { + in = false + a[i] = "//" + break + } + + a[i] = fmt.Sprintf("//\t%s", v[3:]) + default: + if strings.HasPrefix(v, "//
") {
+					a[i] = "//"
+					in = true
+				}
+			}
+		}
+		r = strings.Join(a, "\n")
+	}()
+
+	s = strings.ReplaceAll(s, "\f", "")
+	b := bytesBufferPool.Get().(*bytes.Buffer)
+	defer func() { b.Reset(); bytesBufferPool.Put(b) }()
+	for len(s) != 0 {
+		c := s[0]
+		s = s[1:]
+		if len(s) == 0 {
+			b.WriteByte(c)
+			break
+		}
+
+		if c != '/' {
+			b.WriteByte(c)
+			continue
+		}
+
+		c2 := s[0]
+		s = s[1:]
+		switch c2 {
+		case '/': // line comment start
+			b.WriteByte(c)
+			b.WriteByte(c2)
+			for {
+				c := s[0]
+				s = s[1:]
+				b.WriteByte(c)
+				if c == '\n' {
+					break
+				}
+			}
+		case '*': // block comment start
+			b2 := bytesBufferPool.Get().(*bytes.Buffer)
+			defer func() { b2.Reset(); bytesBufferPool.Put(b2) }()
+			for {
+				c := s[0]
+				s = s[1:]
+				if c != '*' {
+					b2.WriteByte(c)
+					continue
+				}
+
+			more:
+				c2 := s[0]
+				s = s[1:]
+				if c2 == '*' {
+					b2.WriteByte(c)
+					goto more
+				}
+
+				if c2 != '/' {
+					b2.WriteByte(c)
+					b2.WriteByte(c2)
+					continue
+				}
+
+				break
+			}
+			s2 := b2.String() // comment sans /* prefix and */ suffix
+			a := strings.Split(s2, "\n")
+			nl := len(s) != 0 && s[0] == '\n'
+			if len(a) == 1 { // /* foo */ form
+				if nl {
+					s = s[1:]
+					fmt.Fprintf(b, "//%s\n", s2)
+					break
+				}
+
+				fmt.Fprintf(b, "/*%s*/", s2)
+				break
+			}
+
+			if !nl {
+				fmt.Fprintf(b, "/*%s*/", s2)
+				break
+			}
+
+			// Block comment followed by a newline can be safely replaced by a sequence of
+			// line comments.  Try to enhance the comment.
+			if commentForm1(b, a) ||
+				commentForm2(b, a) ||
+				commentForm3(b, a) {
+				break
+			}
+
+			// No enhancement posibilities detected, use the default form.
+			if a[len(a)-1] == "" {
+				a = a[:len(a)-1]
+			}
+			fmt.Fprintf(b, "//%s", a[0])
+			for _, v := range a[1:] {
+				fmt.Fprintf(b, "\n// %s", v)
+			}
+		default:
+			b.WriteByte(c)
+			b.WriteByte(c2)
+		}
+	}
+	return b.String()
+}
+
+func commentForm1(b *bytes.Buffer, a []string) bool {
+	// Example
+	//
+	//	/*
+	//	** Initialize this module.
+	//	**
+	//	** This Tcl module contains only a single new Tcl command named "sqlite".
+	//	** (Hence there is no namespace.  There is no point in using a namespace
+	//	** if the extension only supplies one new name!)  The "sqlite" command is
+	//	** used to open a new SQLite database.  See the DbMain() routine above
+	//	** for additional information.
+	//	**
+	//	** The EXTERN macros are required by TCL in order to work on windows.
+	//	*/
+	if strings.TrimSpace(a[0]) != "" {
+		return false
+	}
+
+	if strings.TrimSpace(a[len(a)-1]) != "" {
+		return false
+	}
+
+	a = a[1 : len(a)-1]
+	if len(a) == 0 {
+		return false
+	}
+
+	for i, v := range a {
+		v = strings.TrimSpace(v)
+		if !strings.HasPrefix(v, "*") {
+			return false
+		}
+
+		a[i] = strings.TrimLeft(v, "*")
+	}
+
+	fmt.Fprintf(b, "//%s", a[0])
+	for _, v := range a[1:] {
+		fmt.Fprintf(b, "\n//%s", v)
+	}
+	return true
+}
+
+func commentForm2(b *bytes.Buffer, a []string) bool {
+	// Example
+	//
+	//	/**************************** sqlite3_column_  *******************************
+	//	** The following routines are used to access elements of the current row
+	//	** in the result set.
+	//	*/
+	if strings.TrimSpace(a[len(a)-1]) != "" {
+		return false
+	}
+
+	a = a[:len(a)-1]
+	if len(a) == 0 {
+		return false
+	}
+
+	for i, v := range a[1:] {
+		v = strings.TrimSpace(v)
+		if !strings.HasPrefix(v, "*") {
+			return false
+		}
+
+		a[i+1] = strings.TrimLeft(v, "*")
+	}
+
+	fmt.Fprintf(b, "// %s", strings.TrimSpace(a[0]))
+	if strings.HasPrefix(a[0], "**") && strings.HasSuffix(a[0], "**") {
+		fmt.Fprintf(b, "\n//")
+	}
+	for _, v := range a[1:] {
+		fmt.Fprintf(b, "\n//%s", v)
+	}
+	return true
+}
+
+func commentForm3(b *bytes.Buffer, a []string) bool {
+	// Example
+	//
+	//	/* Call sqlite3_shutdown() once before doing anything else. This is to
+	//	** test that sqlite3_shutdown() can be safely called by a process before
+	//	** sqlite3_initialize() is. */
+	for i, v := range a[1:] {
+		v = strings.TrimSpace(v)
+		if !strings.HasPrefix(v, "*") {
+			return false
+		}
+
+		a[i+1] = strings.TrimLeft(v, "*")
+	}
+
+	fmt.Fprintf(b, "// %s", strings.TrimSpace(a[0]))
+	if strings.HasPrefix(a[0], "**") && strings.HasSuffix(a[0], "**") {
+		fmt.Fprintf(b, "\n//")
+	}
+	for _, v := range a[1:] {
+		fmt.Fprintf(b, "\n//%s", v)
+	}
+	return true
+}
+
+// Return the preceding white space, including any comments, of the first token
+// of n.
+func tokenSeparator(n cc.Node) (r string) {
+	if n == nil {
+		return ""
+	}
+
+	var tok cc.Token
+	cc.Inspect(n, func(n cc.Node, _ bool) bool {
+		if x, ok := n.(*cc.Token); ok {
+			if a, b := tok.Seq(), x.Seq(); a == 0 || a > x.Seq() && b != 0 {
+				tok = *x
+			}
+		}
+		return true
+	})
+	return tok.Sep.String()
+}
+
+func source(n ...cc.Node) (r string) {
+	if len(n) == 0 {
+		return ""
+	}
+
+	var a []*cc.Token
+	for _, v := range n {
+		cc.Inspect(v, func(n cc.Node, _ bool) bool {
+			if x, ok := n.(*cc.Token); ok && x.Seq() != 0 {
+				a = append(a, x)
+			}
+			return true
+		})
+	}
+	sort.Slice(a, func(i, j int) bool {
+		return a[i].Seq() < a[j].Seq()
+	})
+	w := 0
+	seq := -1
+	for _, v := range a {
+		if n := v.Seq(); n != seq {
+			seq = n
+			a[w] = v
+			w++
+		}
+	}
+	a = a[:w]
+	var b strings.Builder
+	for _, v := range a {
+		b.WriteString(v.Sep.String())
+		b.WriteString(v.Src.String())
+	}
+	return b.String()
+}
+
+type initPatch struct {
+	t    cc.Type
+	init *cc.Initializer
+	fld  cc.Field
+}
+
+type tld struct {
+	name    string // Can differ from the original one.
+	patches []initPatch
+}
+
+type block struct {
+	block  *cc.CompoundStatement
+	decls  []*cc.Declaration // What to declare in this block.
+	params []*cc.Parameter
+	parent *block
+	scope  scope
+
+	noDecl  bool // Locals declared in one of the parent scopes.
+	topDecl bool // Declare locals at block start to avoid "jumps over declaration".
+}
+
+func newBlock(parent *block, n *cc.CompoundStatement, decls []*cc.Declaration, params []*cc.Parameter, scope scope, topDecl bool) *block {
+	return &block{
+		block:   n,
+		decls:   decls,
+		params:  params,
+		parent:  parent,
+		scope:   scope,
+		topDecl: topDecl,
+	}
+}
+
+type local struct {
+	name string
+	off  uintptr // If isPinned: bp+off
+
+	forceRead bool // Possibly never read.
+	isPinned  bool // Prevent this local from being placed in Go movable stack.
+}
+
+type switchState int
+
+const (
+	_                 switchState = iota // Not in switch.
+	inSwitchFirst                        // Before seeing "case/default".
+	inSwitchCase                         // Seen "case/default".
+	inSwitchSeenBreak                    // In switch "case/default" and seen "break/return".
+	inSwitchFlat
+)
+
+type function struct {
+	block            *block
+	blocks           map[*cc.CompoundStatement]*block
+	bpName           string
+	breakCtx         int //TODO merge with continueCtx
+	complits         map[*cc.PostfixExpression]uintptr
+	condInitPrefix   func()
+	continueCtx      int
+	flatLabels       int
+	flatSwitchLabels map[*cc.LabeledStatement]int
+	fndef            *cc.FunctionDefinition
+	gen              *project
+	ifCtx            cc.Node
+	ignore           map[*cc.Declarator]bool // Pseudo declarators
+	labelNames       map[cc.StringID]string
+	labels           scope
+	locals           map[*cc.Declarator]*local
+	off              uintptr         // bp+off allocs
+	params           []*cc.Parameter // May differ from what fndef says
+	project          *project
+	rt               cc.Type // May differ from what fndef says
+	scope            scope
+	switchCtx        switchState
+	tlsName          string
+	top              *block
+	unusedLabels     map[cc.StringID]struct{}
+	vaLists          map[*cc.PostfixExpression]uintptr
+	vaName           string
+	vaType           cc.Type
+	vlas             map[*cc.Declarator]struct{}
+
+	hasJumps            bool
+	mainSignatureForced bool
+}
+
+func newFunction(p *project, n *cc.FunctionDefinition) *function {
+	d := n.Declarator
+	t := d.Type()
+	rt := t.Result()
+	params := t.Parameters()
+	var mainSignatureForced bool
+	var ignore map[*cc.Declarator]bool
+	if d.Name() == idMain && d.Linkage == cc.External {
+		if rt.Kind() != cc.Int {
+			rt = p.task.cfg.ABI.Type(cc.Int)
+		}
+		if len(params) != 2 {
+			mainSignatureForced = true
+			d1 := newDeclarator("argc")
+			t1 := p.task.cfg.ABI.Type(cc.Int)
+			d2 := newDeclarator("argv")
+			t2 := p.task.cfg.ABI.Ptr(n, p.task.cfg.ABI.Type(cc.Void))
+			params = []*cc.Parameter{
+				cc.NewParameter(d1, t1),
+				cc.NewParameter(d2, t2),
+			}
+			ignore = map[*cc.Declarator]bool{d1: true, d2: true}
+		}
+	}
+	f := &function{
+		blocks:              map[*cc.CompoundStatement]*block{},
+		complits:            map[*cc.PostfixExpression]uintptr{},
+		fndef:               n,
+		gen:                 p,
+		hasJumps:            n.CompoundStatement.IsJumpTarget(),
+		ignore:              ignore,
+		locals:              map[*cc.Declarator]*local{},
+		mainSignatureForced: mainSignatureForced,
+		params:              params,
+		project:             p,
+		rt:                  rt,
+		scope:               p.newScope(),
+		unusedLabels:        map[cc.StringID]struct{}{},
+		vaLists:             map[*cc.PostfixExpression]uintptr{},
+		vlas:                map[*cc.Declarator]struct{}{},
+	}
+	f.tlsName = f.scope.take(idTls)
+	if t.IsVariadic() {
+		f.vaName = f.scope.take(idVa)
+	}
+	f.layoutLocals(nil, n.CompoundStatement, params)
+	var extern []cc.StringID
+	for _, v := range n.CompoundStatements() { // testdata/gcc-9.1.0/gcc/testsuite/gcc.c-torture/execute/scope-1.c
+		for _, v := range v.Declarations() {
+			for list := v.InitDeclaratorList; list != nil; list = list.InitDeclaratorList {
+				if d := list.InitDeclarator.Declarator; d != nil && d.IsExtern() {
+					extern = append(extern, d.Name())
+				}
+			}
+		}
+	}
+	for _, v := range n.CompoundStatements() {
+		block := f.blocks[v]
+		for _, v := range extern {
+			if tld := f.project.externs[v]; tld != nil {
+				block.scope.take(cc.String(tld.name))
+			}
+		}
+	}
+	for _, v := range n.CompoundStatements() {
+		f.layoutBlocks(v)
+	}
+	f.renameLabels()
+	f.staticAllocsAndPinned(n.CompoundStatement)
+	return f
+}
+
+func (f *function) flatLabel() int {
+	if f.project.pass1 {
+		return 1
+	}
+
+	f.flatLabels++
+	return f.flatLabels
+}
+
+func (f *function) renameLabels() {
+	var a []cc.StringID
+	for _, v := range f.fndef.Labels {
+		if v.Case != cc.LabeledStatementLabel {
+			continue
+		}
+
+		a = append(a, v.Token.Value)
+		f.unusedLabels[v.Token.Value] = struct{}{}
+	}
+	for _, v := range f.fndef.Gotos {
+		delete(f.unusedLabels, v.Token2.Value)
+	}
+	if len(a) == 0 {
+		return
+	}
+	sort.Slice(a, func(i, j int) bool { return a[i].String() < a[j].String() })
+	f.labels = newScope()
+	f.labelNames = map[cc.StringID]string{}
+	for _, id := range a {
+		f.labelNames[id] = f.labels.take(id)
+	}
+}
+
+func (f *function) staticAllocsAndPinned(n *cc.CompoundStatement) {
+	for _, v := range f.params {
+		switch {
+		case v.Type().Kind() == cc.Array && v.Type().IsVLA():
+			// trc("VLA")
+			f.project.err(f.fndef, "variable length arrays not supported")
+		}
+	}
+
+	//TODO use pass1 for this
+	cc.Inspect(n, func(n cc.Node, entry bool) bool {
+		if !entry {
+			return true
+		}
+
+		switch x := n.(type) {
+		case *cc.CastExpression:
+			switch x.Case {
+			case cc.CastExpressionCast: // '(' TypeName ')' CastExpression
+				if t := x.TypeName.Type(); t != nil && t.Kind() != cc.Void {
+					break
+				}
+
+				if d := x.CastExpression.Declarator(); d != nil {
+					if local := f.locals[d]; local != nil {
+						local.forceRead = true
+					}
+				}
+			}
+		}
+
+		x, ok := n.(*cc.PostfixExpression)
+		if !ok || x.Case != cc.PostfixExpressionCall {
+			return true
+		}
+
+		if x.PostfixExpression == nil || x.PostfixExpression.Operand == nil || x.PostfixExpression.Operand.Type() == nil {
+			return true
+		}
+
+		ft := funcType(x.PostfixExpression.Operand.Type())
+		if ft.Kind() != cc.Function {
+			return true
+		}
+
+		if !ft.IsVariadic() {
+			return true
+		}
+
+		fixedParams := len(ft.Parameters())
+		iArg := 0
+		var need uintptr
+		for list := x.ArgumentExpressionList; list != nil; list, iArg = list.ArgumentExpressionList, iArg+1 {
+			if iArg < fixedParams {
+				continue
+			}
+
+			t := list.AssignmentExpression.Operand.Type()
+			if t.IsIntegerType() {
+				need += 8
+				continue
+			}
+
+			switch t.Kind() {
+			case cc.Array, cc.Ptr, cc.Double, cc.Float, cc.Function:
+				need += 8
+			default:
+				panic(todo("", f.project.pos(x), t, t.Kind()))
+			}
+		}
+		if need != 0 {
+			//TODO- if f.project.task.mingw {
+			//TODO- 	need += 8 // On windows the va list is prefixed with its length
+			//TODO- }
+			va := roundup(f.off, 8)
+			f.vaLists[x] = va
+			f.off = va + need
+		}
+		return true
+	})
+}
+
+func funcType(t cc.Type) cc.Type {
+	if t.Kind() == cc.Ptr {
+		t = t.Elem()
+	}
+	return t
+}
+
+type declarator interface {
+	Declarator() *cc.Declarator
+	cc.Node
+}
+
+func (p *project) isArrayParameterDeclarator(d *cc.Declarator) bool {
+	if d.Type().Kind() == cc.Array {
+		if d.Type().IsVLA() {
+			return false
+		}
+
+		return d.IsParameter
+	}
+
+	return false
+}
+
+func (p *project) isArrayDeclarator(d *cc.Declarator) bool {
+	if d.Type().Kind() == cc.Array {
+		if d.Type().IsVLA() {
+			return false
+		}
+
+		return !d.IsParameter
+	}
+
+	return false
+}
+
+func (p *project) isArrayParameter(n declarator, t cc.Type) bool {
+	if t.Kind() != cc.Array {
+		return false
+	}
+
+	if t.IsVLA() {
+		return false
+	}
+
+	if d := n.Declarator(); d != nil {
+		return d.IsParameter
+	}
+
+	return false
+}
+
+func (p *project) isArrayOrPinnedArray(f *function, n declarator, t cc.Type) (r bool) {
+	if t.Kind() != cc.Array {
+		return false
+	}
+
+	if t.IsVLA() {
+		return false
+	}
+
+	if d := n.Declarator(); d != nil {
+		return !d.IsParameter
+	}
+
+	return p.detectArray(f, n.(cc.Node), true, true, nil)
+}
+
+func (p *project) detectArray(f *function, n cc.Node, pinnedOk, recursiveOk bool, out **cc.Declarator) bool {
+	switch x := n.(type) {
+	case *cc.AssignmentExpression:
+		switch x.Case {
+		case cc.AssignmentExpressionCond: // ConditionalExpression
+			return p.detectArray(f, x.ConditionalExpression, pinnedOk, recursiveOk, out)
+		case cc.AssignmentExpressionAssign: // UnaryExpression '=' AssignmentExpression
+			return p.detectArray(f, x.UnaryExpression, pinnedOk, recursiveOk, out)
+		default:
+			return false
+		}
+	case *cc.ConditionalExpression:
+		switch x.Case {
+		case cc.ConditionalExpressionLOr: // LogicalOrExpression
+			return p.detectArray(f, x.LogicalOrExpression, pinnedOk, recursiveOk, out)
+		case cc.ConditionalExpressionCond: // LogicalOrExpression '?' Expression ':' ConditionalExpression
+			return p.detectArray(f, x.LogicalOrExpression, pinnedOk, recursiveOk, out) ||
+				p.detectArray(f, x.Expression, pinnedOk, recursiveOk, out) ||
+				p.detectArray(f, x.ConditionalExpression, pinnedOk, recursiveOk, out)
+		default:
+			panic(todo("", p.pos(x), x.Case))
+		}
+	case *cc.LogicalOrExpression:
+		switch x.Case {
+		case cc.LogicalOrExpressionLAnd: // LogicalAndExpression
+			return p.detectArray(f, x.LogicalAndExpression, pinnedOk, recursiveOk, out)
+		default:
+			return false
+		}
+	case *cc.LogicalAndExpression:
+		switch x.Case {
+		case cc.LogicalAndExpressionOr: // InclusiveOrExpression
+			return p.detectArray(f, x.InclusiveOrExpression, pinnedOk, recursiveOk, out)
+		default:
+			return false
+		}
+	case *cc.InclusiveOrExpression:
+		switch x.Case {
+		case cc.InclusiveOrExpressionXor: // ExclusiveOrExpression
+			return p.detectArray(f, x.ExclusiveOrExpression, pinnedOk, recursiveOk, out)
+		default:
+			return false
+		}
+	case *cc.ExclusiveOrExpression:
+		switch x.Case {
+		case cc.ExclusiveOrExpressionAnd: // AndExpression
+			return p.detectArray(f, x.AndExpression, pinnedOk, recursiveOk, out)
+		default:
+			return false
+		}
+	case *cc.AndExpression:
+		switch x.Case {
+		case cc.AndExpressionEq: // EqualityExpression
+			return p.detectArray(f, x.EqualityExpression, pinnedOk, recursiveOk, out)
+		default:
+			return false
+		}
+	case *cc.EqualityExpression:
+		switch x.Case {
+		case cc.EqualityExpressionRel: // RelationalExpression
+			return p.detectArray(f, x.RelationalExpression, pinnedOk, recursiveOk, out)
+		default:
+			return false
+		}
+	case *cc.RelationalExpression:
+		switch x.Case {
+		case cc.RelationalExpressionShift: // ShiftExpression
+			return p.detectArray(f, x.ShiftExpression, pinnedOk, recursiveOk, out)
+		default:
+			return false
+		}
+	case *cc.ShiftExpression:
+		switch x.Case {
+		case cc.ShiftExpressionAdd: // AdditiveExpression
+			return p.detectArray(f, x.AdditiveExpression, pinnedOk, recursiveOk, out)
+		default:
+			return false
+		}
+	case *cc.AdditiveExpression:
+		switch x.Case {
+		case cc.AdditiveExpressionMul: // MultiplicativeExpression
+			return p.detectArray(f, x.MultiplicativeExpression, pinnedOk, recursiveOk, out)
+		case
+			cc.AdditiveExpressionSub, // AdditiveExpression '-' MultiplicativeExpression
+			cc.AdditiveExpressionAdd: // AdditiveExpression '+' MultiplicativeExpression
+
+			return p.detectArray(f, x.AdditiveExpression, pinnedOk, recursiveOk, out) || p.detectArray(f, x.MultiplicativeExpression, pinnedOk, recursiveOk, out)
+		default:
+			panic(todo("", p.pos(x), x.Case))
+		}
+	case *cc.MultiplicativeExpression:
+		switch x.Case {
+		case cc.MultiplicativeExpressionCast: // CastExpression
+			return p.detectArray(f, x.CastExpression, pinnedOk, recursiveOk, out)
+		default:
+			return false
+		}
+	case *cc.CastExpression:
+		switch x.Case {
+		case cc.CastExpressionUnary: // UnaryExpression
+			return p.detectArray(f, x.UnaryExpression, pinnedOk, recursiveOk, out)
+		case cc.CastExpressionCast: // '(' TypeName ')' CastExpression
+			return p.detectArray(f, x.CastExpression, pinnedOk, recursiveOk, out)
+		default:
+			panic(todo("", p.pos(x), x.Case))
+		}
+	case *cc.UnaryExpression:
+		switch x.Case {
+		case cc.UnaryExpressionPostfix: // PostfixExpression
+			return p.detectArray(f, x.PostfixExpression, pinnedOk, recursiveOk, out)
+		case
+			cc.UnaryExpressionDeref,  // '*' CastExpression
+			cc.UnaryExpressionAddrof: // '&' CastExpression
+
+			return p.detectArray(f, x.CastExpression, pinnedOk, recursiveOk, out)
+		case
+			cc.UnaryExpressionSizeofExpr,  // "sizeof" UnaryExpression
+			cc.UnaryExpressionSizeofType,  // "sizeof" '(' TypeName ')'
+			cc.UnaryExpressionMinus,       // '-' CastExpression
+			cc.UnaryExpressionCpl,         // '~' CastExpression
+			cc.UnaryExpressionAlignofExpr, // "_Alignof" UnaryExpression
+			cc.UnaryExpressionAlignofType, // "_Alignof" '(' TypeName ')'
+			cc.UnaryExpressionNot,         // '!' CastExpression
+			cc.UnaryExpressionInc,         // "++" UnaryExpression
+			cc.UnaryExpressionDec,         // "--" UnaryExpression
+			cc.UnaryExpressionPlus:        // '+' CastExpression
+
+			return false
+		default:
+			panic(todo("", p.pos(x), x.Case))
+		}
+	case *cc.PostfixExpression:
+		switch x.Case {
+		case cc.PostfixExpressionPrimary: // PrimaryExpression
+			return p.detectArray(f, x.PrimaryExpression, pinnedOk, recursiveOk, out)
+		case cc.PostfixExpressionIndex: // PostfixExpression '[' Expression ']'
+			return recursiveOk && p.detectArray(f, x.PostfixExpression, pinnedOk, recursiveOk, out)
+		case
+			cc.PostfixExpressionSelect,  // PostfixExpression '.' IDENTIFIER
+			cc.PostfixExpressionDec,     // PostfixExpression "--"
+			cc.PostfixExpressionInc,     // PostfixExpression "++"
+			cc.PostfixExpressionCall,    // PostfixExpression '(' ArgumentExpressionList ')'
+			cc.PostfixExpressionComplit, // '(' TypeName ')' '{' InitializerList ',' '}'
+			cc.PostfixExpressionPSelect: // PostfixExpression "->" IDENTIFIER
+
+			return false
+		default:
+			panic(todo("", p.pos(x), x.Case))
+		}
+	case *cc.PrimaryExpression:
+		switch x.Case {
+		case
+			cc.PrimaryExpressionString,  // STRINGLITERAL
+			cc.PrimaryExpressionEnum,    // ENUMCONST
+			cc.PrimaryExpressionChar,    // CHARCONST
+			cc.PrimaryExpressionLChar,   // LONGCHARCONST
+			cc.PrimaryExpressionLString, // LONGSTRINGLITERAL
+			cc.PrimaryExpressionFloat,   // FLOATCONST
+			cc.PrimaryExpressionInt:     // INTCONST
+
+			return false
+		case cc.PrimaryExpressionIdent: // IDENTIFIER
+			d := x.Declarator()
+			if d == nil || d.IsParameter {
+				return false
+			}
+
+			if d.Type().Kind() != cc.Array {
+				return false
+			}
+
+			if d.Type().IsVLA() {
+				return false
+			}
+
+			if pinnedOk {
+				if out != nil {
+					*out = d
+				}
+				return true
+			}
+
+			local := f.locals[d]
+			if local == nil || local.isPinned {
+				return false
+			}
+
+			if out != nil {
+				*out = d
+			}
+			return true
+		case cc.PrimaryExpressionExpr: // '(' Expression ')'
+			return p.detectArray(f, x.Expression, pinnedOk, recursiveOk, out)
+		case cc.PrimaryExpressionStmt: // '(' CompoundStatement ')'
+			p.err(x, "statement expressions not supported")
+			return false
+		default:
+			panic(todo("", p.pos(x), x.Case))
+		}
+	case *cc.Expression:
+		switch x.Case {
+		case cc.ExpressionAssign: // AssignmentExpression
+			return p.detectArray(f, x.AssignmentExpression, pinnedOk, recursiveOk, out)
+		case cc.ExpressionComma: // Expression ',' AssignmentExpression
+			return p.detectArray(f, x.Expression, pinnedOk, recursiveOk, out) || p.detectArray(f, x.AssignmentExpression, pinnedOk, recursiveOk, out)
+		default:
+			panic(todo("", p.pos(x), x.Case))
+		}
+	default:
+		panic(todo("%T", x))
+	}
+}
+
+func (p *project) isArray(f *function, n declarator, t cc.Type) (r bool) {
+	if t.Kind() != cc.Array {
+		return false
+	}
+
+	if t.IsVLA() {
+		return false
+	}
+
+	if f == nil {
+		return true
+	}
+
+	if d := n.Declarator(); d != nil {
+		local := f.locals[d]
+		return !d.IsParameter && (local == nil || !local.isPinned)
+	}
+
+	return p.detectArray(f, n.(cc.Node), false, true, nil)
+}
+
+// Return n's position with path reduced to baseName(path) unless
+// p.task.fullPathComments is true.
+func (p *project) pos(n cc.Node) (r token.Position) {
+	if n == nil {
+		return r
+	}
+
+	r = token.Position(n.Position())
+	if r.IsValid() && !p.task.fullPathComments {
+		r.Filename = filepath.Base(r.Filename)
+	}
+	return r
+}
+
+// Return n's position with path reduced to baseName(path).
+func pos(n cc.Node) (r token.Position) {
+	if n == nil {
+		return r
+	}
+
+	r = token.Position(n.Position())
+	if r.IsValid() {
+		r.Filename = filepath.Base(r.Filename)
+	}
+	return r
+}
+
+func roundup(n, to uintptr) uintptr {
+	if r := n % to; r != 0 {
+		return n + to - r
+	}
+
+	return n
+}
+
+func (f *function) pin(n cc.Node, d *cc.Declarator) {
+	local := f.locals[d]
+	if local == nil || local.isPinned {
+		return
+	}
+
+	local.isPinned = true
+	if oTracePin || f.project.task.tracePinning {
+		fmt.Printf("%v: %s at %v: is pinned (%v)\n", n.Position(), d.Name(), d.Position(), origin(2))
+	}
+	local.off = roundup(f.off, uintptr(d.Type().Align()))
+	f.off = local.off + paramTypeDecay(d).Size()
+}
+
+func paramTypeDecay(d *cc.Declarator) (r cc.Type) {
+	r = d.Type()
+	if d.IsParameter && r.Kind() == cc.Array {
+		r = r.Decay()
+	}
+	return r
+}
+
+func (f *function) layoutBlocks(n *cc.CompoundStatement) {
+	block := f.blocks[n]
+	type item struct {
+		ds *cc.DeclarationSpecifiers
+		d  *cc.Declarator
+	}
+	var work []item
+	for _, v := range block.params {
+		if v.Type().Kind() == cc.Void {
+			break
+		}
+
+		work = append(work, item{nil, v.Declarator()})
+	}
+	for _, decl := range block.decls {
+		ds := decl.DeclarationSpecifiers
+		for list := decl.InitDeclaratorList; list != nil; list = list.InitDeclaratorList {
+			work = append(work, item{ds, list.InitDeclarator.Declarator})
+		}
+	}
+	block.scope.take(cc.String(f.tlsName))
+	if f.vaName != "" {
+		block.scope.take(cc.String(f.vaName))
+	}
+	for _, item := range work {
+		d := item.d
+		if f.ignore[d] {
+			continue
+		}
+
+		if !f.ignore[d] && d.IsStatic() {
+			continue
+		}
+
+		if d.IsFunctionPrototype() || d.IsExtern() {
+			continue
+		}
+
+		local := &local{forceRead: d.Read == 0}
+		if t := d.Type(); t != nil && t.Name() == idVaList {
+			local.forceRead = true
+		}
+		f.locals[d] = local
+		local.name = block.scope.take(d.Name())
+	}
+}
+
+func (f *function) layoutLocals(parent *block, n *cc.CompoundStatement, params []*cc.Parameter) {
+	block := newBlock(parent, n, n.Declarations(), params, f.project.newScope(), n.IsJumpTarget())
+	f.blocks[n] = block
+	if parent == nil {
+		f.top = block
+		f.top.topDecl = f.hasJumps
+	}
+	for _, ch := range n.Children() {
+		f.layoutLocals(block, ch, nil)
+		if f.hasJumps {
+			chb := f.blocks[ch]
+			chb.noDecl = true
+			f.top.decls = append(f.top.decls, chb.decls...)
+			chb.decls = nil
+		}
+	}
+}
+
+func newDeclarator(name string) *cc.Declarator {
+	return &cc.Declarator{
+		DirectDeclarator: &cc.DirectDeclarator{
+			Case:  cc.DirectDeclaratorIdent,
+			Token: cc.Token{Rune: cc.IDENTIFIER, Value: cc.String(name)},
+		},
+	}
+}
+
+type enumSpec struct {
+	decl *cc.Declaration
+	spec *cc.EnumSpecifier
+
+	emitted bool
+}
+
+func (n *enumSpec) emit(p *project) {
+	if n == nil || p.pass1 || n.emitted {
+		return
+	}
+
+	n.emitted = true
+	ok := false
+	for list := n.spec.EnumeratorList; list != nil; list = list.EnumeratorList {
+		nm := list.Enumerator.Token.Value
+		if _, ok2 := p.emitedEnums[nm]; !ok2 && p.enumConsts[nm] != "" {
+			ok = true
+			break
+		}
+	}
+	if !ok {
+		return
+	}
+
+	p.w("%s", tidyComment("\n", n.decl))
+	p.w("const ( /* %v: */", p.pos(n.decl))
+	for list := n.spec.EnumeratorList; list != nil; list = list.EnumeratorList {
+		en := list.Enumerator
+		nm := en.Token.Value
+		if _, ok := p.emitedEnums[nm]; ok || p.enumConsts[nm] == "" {
+			continue
+		}
+
+		p.emitedEnums[nm] = struct{}{}
+		p.w("%s%s = ", tidyComment("\n", en), p.enumConsts[nm])
+		p.intConst(en, "", en.Operand, en.Operand.Type(), fForceNoConv)
+		p.w(";")
+	}
+	p.w(");")
+}
+
+type typedef struct {
+	sig uint64
+	tld *tld
+}
+
+type define struct {
+	name  string
+	value cc.Value
+}
+
+type project struct {
+	ast                *cc.AST
+	buf                bytes.Buffer
+	capi               []string
+	defines            map[cc.StringID]define
+	defineLines        []string
+	emitedEnums        map[cc.StringID]struct{}
+	enumConsts         map[cc.StringID]string
+	enumSpecs          map[*cc.EnumSpecifier]*enumSpec
+	errors             scanner.ErrorList
+	externs            map[cc.StringID]*tld
+	fn                 string
+	imports            map[string]*imported // C name: import info
+	intType            cc.Type
+	localTaggedStructs []func()
+	mainName           string
+	ptrSize            uintptr
+	ptrType            cc.Type
+	scope              scope
+	sharedFns          map[*cc.FunctionDefinition]struct{}
+	sharedFnsEmitted   map[*cc.FunctionDefinition]struct{}
+	staticQueue        []*cc.InitDeclarator
+	structs            map[cc.StringID]*taggedStruct // key: C tag
+	symtab             map[string]interface{}        // *tld or *imported
+	task               *Task
+	tldScope           scope
+	tlds               map[*cc.Declarator]*tld
+	ts                 bytes.Buffer // Text segment
+	tsName             string
+	tsNameP            string
+	tsOffs             map[cc.StringID]uintptr
+	tsW                []rune // Text segment, wchar_t
+	tsWName            string
+	tsWNameP           string
+	tsWOffs            map[cc.StringID]uintptr
+	typeSigHash        maphash.Hash
+	typedefTypes       map[cc.StringID]*typedef
+	typedefsEmited     map[string]struct{}
+	verifyStructs      map[string]cc.Type
+	wanted             map[*cc.Declarator]struct{}
+	wcharSize          uintptr
+
+	isMain bool
+	pass1  bool
+}
+
+func newProject(t *Task) (*project, error) {
+	voidType := t.cfg.ABI.Type(cc.Void)
+	ptrType := t.cfg.ABI.Ptr(nil, voidType)
+	intType := t.cfg.ABI.Type(cc.Int)
+	if intType.Size() != 4 { // We're assuming wchar_t is int32.
+		return nil, fmt.Errorf("unsupported C int size: %d", intType.Size())
+	}
+
+	if n := t.cfg.ABI.Types[cc.UChar].Size; n != 1 {
+		return nil, fmt.Errorf("unsupported C unsigned char size: %d", n)
+	}
+
+	if n := t.cfg.ABI.Types[cc.UShort].Size; n != 2 {
+		return nil, fmt.Errorf("unsupported C unsigned short size: %d", n)
+	}
+
+	if n := t.cfg.ABI.Types[cc.UInt].Size; n != 4 {
+		return nil, fmt.Errorf("unsupported C unsigned int size: %d", n)
+	}
+
+	if n := t.cfg.ABI.Types[cc.ULongLong].Size; n != 8 {
+		return nil, fmt.Errorf("unsupported C unsigned long long size: %d", n)
+	}
+
+	p := &project{
+		defines:          map[cc.StringID]define{},
+		emitedEnums:      map[cc.StringID]struct{}{},
+		enumConsts:       map[cc.StringID]string{},
+		enumSpecs:        map[*cc.EnumSpecifier]*enumSpec{},
+		externs:          map[cc.StringID]*tld{},
+		imports:          map[string]*imported{},
+		intType:          intType,
+		ptrSize:          t.cfg.ABI.Types[cc.Ptr].Size,
+		ptrType:          ptrType,
+		scope:            newScope(),
+		sharedFns:        t.cfg.SharedFunctionDefinitions.M,
+		sharedFnsEmitted: map[*cc.FunctionDefinition]struct{}{},
+		symtab:           map[string]interface{}{},
+		task:             t,
+		tlds:             map[*cc.Declarator]*tld{},
+		tsWOffs:          map[cc.StringID]uintptr{},
+		tsOffs:           map[cc.StringID]uintptr{},
+		typedefTypes:     map[cc.StringID]*typedef{},
+		typedefsEmited:   map[string]struct{}{},
+		verifyStructs:    map[string]cc.Type{},
+		wanted:           map[*cc.Declarator]struct{}{},
+		wcharSize:        t.asts[0].WideCharType.Size(),
+	}
+	p.tldScope = p.scope
+	p.scope.take(idCAPI)
+	for _, v := range t.imported {
+		var err error
+		if v.name, v.exports, err = t.capi(v.path); err != nil {
+			return nil, err
+		}
+
+		v.qualifier = p.scope.take(cc.String(v.name)) + "."
+		for k := range v.exports {
+			if p.imports[k] == nil {
+				p.imports[k] = v
+			}
+		}
+	}
+	p.tsNameP = p.scope.take(idTs)
+	p.tsName = p.scope.take(idTs)
+	p.tsWNameP = p.scope.take(idWtext)
+	p.tsWName = p.scope.take(idWtext)
+	if err := p.layout(); err != nil {
+		return nil, err
+	}
+
+	return p, nil
+}
+
+func (p *project) newScope() scope {
+	s := newScope()
+	var a []cc.StringID
+	for k := range p.structs {
+		a = append(a, k)
+	}
+	sort.Slice(a, func(i, j int) bool { return a[i].String() < a[j].String() })
+	for _, k := range a {
+		s.take(cc.String(p.structs[k].name))
+	}
+	return s
+}
+
+func (p *project) err(n cc.Node, s string, args ...interface{}) {
+	if p.task.errTrace || strings.Contains(s, "internal error") {
+		s = s + "(" + origin(2) + ")"
+	}
+	if p.task.traceTranslationUnits {
+		trc("%v: error: %s (%v)", pos(n), fmt.Sprintf(s, args...), origin(2))
+	}
+	if !p.task.allErrors && len(p.errors) >= 10 {
+		return
+	}
+
+	switch {
+	case n == nil:
+		p.errors.Add(token.Position{}, fmt.Sprintf(s, args...))
+	default:
+		p.errors.Add(token.Position(n.Position()), fmt.Sprintf(s, args...))
+		if !p.task.allErrors && len(p.errors) == 10 {
+			p.errors.Add(token.Position(n.Position()), tooManyErrors)
+		}
+	}
+}
+
+func (p *project) o(s string, args ...interface{}) {
+	if oTraceG {
+		fmt.Printf(s, args...)
+	}
+	fmt.Fprintf(p.task.out, s, args...)
+}
+
+func (p *project) w(s string, args ...interface{}) {
+	if p.pass1 {
+		return
+	}
+
+	if coverExperiment {
+		pc, _, _, ok := runtime.Caller(1)
+		if ok {
+			coverMap[pc] = struct{}{}
+		}
+	}
+	if oTraceW {
+		fmt.Printf(s, args...)
+	}
+	//fmt.Fprintf(&p.buf, "/* %s */", origin(2)) //TODO-
+	fmt.Fprintf(&p.buf, s, args...)
+}
+
+func (p *project) layout() error {
+	if err := p.layoutTLDs(); err != nil {
+		return err
+	}
+
+	if err := p.layoutSymtab(); err != nil {
+		return err
+	}
+
+	if err := p.layoutStructs(); err != nil {
+		return err
+	}
+
+	if err := p.layoutEnums(); err != nil {
+		return err
+	}
+
+	if err := p.layoutDefines(); err != nil {
+		return err
+	}
+
+	return p.layoutStaticLocals()
+}
+
+func (p *project) layoutSymtab() error {
+	var t0 time.Time
+	if p.task.traceTranslationUnits {
+		fmt.Printf("processing symbol table ... ")
+		t0 = time.Now()
+		defer func() { fmt.Println(time.Since(t0)) }()
+	}
+
+	for _, i := range p.task.symSearchOrder {
+		switch {
+		case i < 0:
+			imported := p.task.imported[-i-1]
+			for nm := range imported.exports {
+				if _, ok := p.symtab[nm]; !ok {
+					p.symtab[nm] = imported
+				}
+			}
+		default:
+			ast := p.task.asts[i]
+			for d := range ast.TLD {
+				if d.IsFunctionPrototype() || d.Linkage != cc.External {
+					continue
+				}
+
+				nm := d.Name()
+				name := nm.String()
+				if _, ok := p.symtab[name]; !ok {
+					tld := p.externs[nm]
+					if tld == nil {
+						if d.Type().Kind() != cc.Function && !p.task.header {
+							p.err(d, "back-end: undefined: %s %v %v", d.Name(), d.Type(), d.Type().Kind())
+						}
+						continue
+					}
+
+					p.symtab[name] = tld
+				}
+			}
+		}
+	}
+	return nil
+}
+
+func (p *project) layoutDefines() error {
+	if !p.task.exportDefinesValid {
+		return nil
+	}
+
+	var t0 time.Time
+	if p.task.traceTranslationUnits {
+		fmt.Printf("processing #defines ... ")
+		t0 = time.Now()
+		defer func() { fmt.Println(time.Since(t0)) }()
+	}
+
+	var prefix = p.task.exportDefines
+	taken := map[cc.StringID]struct{}{}
+	for _, ast := range p.task.asts {
+		var a []cc.StringID
+		for nm, m := range ast.Macros {
+			if m.IsFnLike() {
+				continue
+			}
+
+			if strings.HasPrefix(nm.String(), "__") {
+				continue
+			}
+
+			if _, ok := taken[nm]; ok {
+				continue
+			}
+
+			taken[nm] = struct{}{}
+			a = append(a, nm)
+		}
+		sort.Slice(a, func(i, j int) bool { return a[i].String() < a[j].String() })
+		for _, nm := range a {
+			m := ast.Macros[nm]
+			val, src := evalMacro(m, ast)
+			if src == "" {
+				continue
+			}
+
+			name := nm.String()
+			switch {
+			case prefix == "":
+				name = capitalize(name)
+			default:
+				name = prefix + name
+			}
+			name = p.scope.take(cc.String(name))
+			p.defines[nm] = define{name, val}
+			p.defineLines = append(p.defineLines, fmt.Sprintf("%s = %s", name, src))
+		}
+	}
+	return nil
+}
+
+func evalMacro(m *cc.Macro, ast *cc.AST) (cc.Value, string) {
+	toks := m.ReplacementTokens()
+	if len(toks) != 1 {
+		return evalMacro2(m, ast)
+	}
+
+	src := strings.TrimSpace(toks[0].Src.String())
+	if len(src) == 0 {
+		return nil, ""
+	}
+
+	neg := ""
+	switch src[0] {
+	case '"':
+		if _, err := strconv.Unquote(src); err == nil {
+			return cc.StringValue(cc.String(src)), src
+		}
+	case '-':
+		neg = "-"
+		src = src[1:]
+		fallthrough
+	default:
+		src = strings.TrimRight(src, "lLuU")
+		if u64, err := strconv.ParseUint(src, 0, 64); err == nil {
+			switch {
+			case neg == "":
+				return cc.Uint64Value(u64), src
+			default:
+				return cc.Int64Value(-u64), neg + src
+			}
+		}
+
+		src = strings.TrimRight(src, "fF")
+		if f64, err := strconv.ParseFloat(src, 64); err == nil {
+			return cc.Float64Value(f64), neg + src
+		}
+	}
+
+	return evalMacro2(m, ast)
+}
+
+func evalMacro2(m *cc.Macro, ast *cc.AST) (cc.Value, string) {
+	op, err := ast.Eval(m)
+	if err != nil {
+		return nil, ""
+	}
+
+	switch x := op.Value().(type) {
+	case cc.Int64Value:
+		return op.Value(), fmt.Sprintf("%d", int64(x))
+	case cc.Uint64Value:
+		return op.Value(), fmt.Sprintf("%d", uint64(x))
+	default:
+		panic(todo("", pos(m)))
+	}
+}
+
+func (p *project) layoutEnums() error {
+	var t0 time.Time
+	if p.task.traceTranslationUnits {
+		fmt.Printf("processing enum values ... ")
+		t0 = time.Now()
+		defer func() { fmt.Println(time.Since(t0)) }()
+	}
+
+	export := doNotChange
+	if p.task.exportEnumsValid {
+		switch {
+		case p.task.exportEnums != "":
+			export = exportPrefix
+		default:
+			export = exportCapitalize
+		}
+	} else if p.task.defaultUnExport {
+		export = doNotExport
+	}
+
+	var enumList []*cc.EnumSpecifier
+	for _, v := range p.task.asts {
+		for list := v.TranslationUnit; list != nil; list = list.TranslationUnit {
+			decl := list.ExternalDeclaration
+			switch decl.Case {
+			case cc.ExternalDeclarationDecl: // Declaration
+				// ok
+			default:
+				continue
+			}
+
+			cc.Inspect(decl.Declaration.DeclarationSpecifiers, func(n cc.Node, entry bool) bool {
+				if !entry {
+					return true
+				}
+
+				x, ok := n.(*cc.EnumSpecifier)
+				if !ok || x.Case != cc.EnumSpecifierDef {
+					return true
+				}
+
+				if _, ok := p.enumSpecs[x]; !ok {
+					enumList = append(enumList, x)
+					p.enumSpecs[x] = &enumSpec{decl: decl.Declaration, spec: x}
+				}
+				return true
+			})
+		}
+	}
+
+	vals := map[cc.StringID]interface{}{}
+	for _, v := range enumList {
+		for list := v.EnumeratorList; list != nil; list = list.EnumeratorList {
+			en := list.Enumerator
+			nm := en.Token.Value
+			var val int64
+			switch x := en.Operand.Value().(type) {
+			case cc.Int64Value:
+				val = int64(x)
+			case cc.Uint64Value:
+				val = int64(x)
+			default:
+				panic(todo(""))
+			}
+			switch ex, ok := vals[nm]; {
+			case ok:
+				switch {
+				case ex == nil: //
+					continue
+				case ex == val: // same name and same value
+					continue
+				default: // same name, different value
+					vals[nm] = nil
+				}
+			default:
+				vals[nm] = val
+			}
+			p.enumConsts[nm] = ""
+		}
+	}
+	var a []cc.StringID
+	for nm := range p.enumConsts {
+		if val, ok := vals[nm]; ok && val == nil {
+			delete(p.enumConsts, nm)
+			continue
+		}
+
+		a = append(a, nm)
+	}
+	sort.Slice(a, func(i, j int) bool { return a[i].String() < a[j].String() })
+	for _, nm := range a {
+		name := nm.String()
+		switch export {
+		case doNotExport:
+			name = unCapitalize(name)
+		case doNotChange:
+			// nop
+		case exportCapitalize:
+			name = capitalize(name)
+		case exportPrefix:
+			name = p.task.exportEnums + name
+		}
+		name = p.scope.take(cc.String(name))
+		p.enumConsts[nm] = name
+	}
+	return nil
+}
+
+func (p *project) layoutStaticLocals() error {
+	var t0 time.Time
+	if p.task.traceTranslationUnits {
+		fmt.Printf("processing static local declarations ... ")
+		t0 = time.Now()
+		defer func() { fmt.Println(time.Since(t0)) }()
+	}
+	for _, v := range p.task.asts {
+		for list := v.TranslationUnit; list != nil; list = list.TranslationUnit {
+			decl := list.ExternalDeclaration
+			switch decl.Case {
+			case cc.ExternalDeclarationFuncDef: // FunctionDefinition
+				// ok
+			default:
+				continue
+			}
+
+			cc.Inspect(decl.FunctionDefinition.CompoundStatement, func(n cc.Node, entry bool) bool {
+				switch x := n.(type) {
+				case *cc.Declarator:
+					if !entry || !x.IsStatic() || x.Read == 0 || x.IsParameter {
+						break
+					}
+
+					nm := x.Name()
+					if s := p.task.staticLocalsPrefix; s != "" {
+						nm = cc.String(s + nm.String())
+					}
+					p.tlds[x] = &tld{name: p.scope.take(nm)}
+				}
+				return true
+			})
+		}
+	}
+	return nil
+}
+
+func (p *project) layoutStructs() error {
+	var t0 time.Time
+	if p.task.traceTranslationUnits {
+		fmt.Printf("processing struct/union types ... ")
+		t0 = time.Now()
+		defer func() { fmt.Println(time.Since(t0)) }()
+	}
+
+	export := doNotChange
+	if p.task.exportStructsValid {
+		switch {
+		case p.task.exportStructs != "":
+			export = exportPrefix
+		default:
+			export = exportCapitalize
+		}
+	} else if p.task.defaultUnExport {
+		export = doNotExport
+	}
+
+	m := map[cc.StringID]*taggedStruct{}
+	var tags []cc.StringID
+	for _, v := range p.task.asts {
+		cc.Inspect(v.TranslationUnit, func(n cc.Node, entry bool) bool {
+			if entry {
+				switch x := n.(type) {
+				case *cc.Declarator:
+					if nm := x.Name().String(); strings.HasPrefix(nm, "_") {
+						break
+					}
+
+					p.captureStructTags(x, x.Type(), m, &tags)
+				case *cc.Declaration:
+					cc.Inspect(x.DeclarationSpecifiers, func(nn cc.Node, entry bool) bool {
+						switch y := nn.(type) {
+						case *cc.StructOrUnionSpecifier:
+							if tag := y.Token.Value; tag != 0 {
+								p.captureStructTags(y, y.Type(), m, &tags)
+							}
+						}
+						return true
+					})
+				}
+			}
+			return true
+		})
+	}
+	sort.Slice(tags, func(i, j int) bool { return tags[i].String() < tags[j].String() })
+	for _, k := range tags {
+		v := m[k]
+		//TODO rename conflicts
+		if v.conflicts {
+			delete(m, k)
+			continue
+		}
+
+		name := k.String()
+		switch export {
+		case doNotExport:
+			name = unCapitalize(name)
+		case doNotChange:
+			// nop
+		case exportCapitalize:
+			name = capitalize(name)
+		case exportPrefix:
+			name = p.task.exportStructs + name
+		}
+		v.name = p.scope.take(cc.String(name))
+	}
+	for _, k := range tags {
+		v := m[k]
+		if v != nil {
+			v.gotyp = p.structType(nil, v.ctyp)
+		}
+	}
+	p.structs = m
+	return nil
+}
+
+func (p *project) captureStructTags(n cc.Node, t cc.Type, m map[cc.StringID]*taggedStruct, tags *[]cc.StringID) {
+	if t == nil {
+		return
+	}
+
+	t = t.Alias()
+	for t.Kind() == cc.Ptr {
+		t = t.Alias().Elem().Alias()
+	}
+	if t.Kind() == cc.Invalid || t.IsIncomplete() {
+		return
+	}
+
+	switch t.Kind() {
+	case cc.Struct, cc.Union:
+		tag := t.Tag()
+		if tag == 0 {
+			return
+		}
+
+		ex := m[tag]
+		if ex != nil {
+			ts := p.typeSignature(n, t)
+			exs := p.typeSignature(n, ex.ctyp)
+			if ts != exs {
+				ex.conflicts = true
+			}
+			return
+		}
+
+		nf := t.NumField()
+		m[tag] = &taggedStruct{ctyp: t, node: n}
+		for idx := []int{0}; idx[0] < nf; idx[0]++ {
+			p.captureStructTags(n, t.FieldByIndex(idx).Type(), m, tags)
+		}
+		*tags = append(*tags, tag)
+	case cc.Array:
+		p.captureStructTags(n, t.Elem(), m, tags)
+	}
+}
+
+func (p *project) typeSignature(n cc.Node, t cc.Type) (r uint64) {
+	p.typeSigHash.Reset()
+	p.typeSignature2(n, &p.typeSigHash, t)
+	return p.typeSigHash.Sum64()
+}
+
+func (p *project) typeSignature2(n cc.Node, b *maphash.Hash, t cc.Type) {
+	t = t.Alias()
+	if t.IsIntegerType() {
+		if !t.IsSignedType() {
+			b.WriteByte('u')
+		}
+		fmt.Fprintf(b, "int%d", t.Size()*8)
+		return
+	}
+
+	if t.IsArithmeticType() {
+		b.WriteString(t.Kind().String())
+		return
+	}
+
+	structOrUnion := "struct"
+	switch t.Kind() {
+	case cc.Ptr:
+		fmt.Fprintf(b, "*%s", t.Elem())
+	case cc.Array:
+		if t.IsVLA() {
+			// trc("VLA")
+			p.err(n, "variable length arrays not supported: %v", t)
+		}
+
+		fmt.Fprintf(b, "[%d]%s", t.Len(), t.Elem())
+	case cc.Vector:
+		fmt.Fprintf(b, "[%d]%s", t.Len(), t.Elem())
+	case cc.Union:
+		structOrUnion = "union"
+		fallthrough
+	case cc.Struct:
+		b.WriteString(structOrUnion)
+		nf := t.NumField()
+		fmt.Fprintf(b, " %d{", nf)
+		b.WriteByte('{')
+		for idx := []int{0}; idx[0] < nf; idx[0]++ {
+			f := t.FieldByIndex(idx)
+			fmt.Fprintf(b, "%s:%d:%d:%v:%d:%d:",
+				f.Name(), f.BitFieldOffset(), f.BitFieldWidth(), f.IsBitField(), f.Offset(), f.Padding(),
+			)
+			p.typeSignature2(f.Declarator(), b, f.Type())
+			b.WriteByte(';')
+		}
+		b.WriteByte('}')
+	case cc.Void:
+		b.WriteString("void")
+	case cc.Invalid:
+		b.WriteString("invalid") //TODO fix cc/v3
+	default:
+		panic(todo("", p.pos(n), t, t.Kind()))
+	}
+}
+
+func (p *project) structType(n cc.Node, t cc.Type) string {
+	switch t.Kind() {
+	case cc.Struct, cc.Union:
+		tag := t.Tag()
+		if tag != 0 && p.structs != nil {
+			s := p.structs[tag]
+			if s == nil {
+				return p.structLiteral(n, t)
+			}
+
+			if s.gotyp == "" {
+				s.gotyp = p.structLiteral(n, t)
+			}
+			return s.gotyp
+		}
+
+		return p.structLiteral(n, t)
+	default:
+		panic(todo("internal error: %v", t.Kind()))
+	}
+}
+
+func (p *project) padName(n *int) string {
+	if !p.task.exportFieldsValid {
+		return "_"
+	}
+
+	*n++
+	return fmt.Sprintf("%s__ccgo_pad%d", p.task.exportFields, *n)
+}
+
+func (p *project) structLiteral(n cc.Node, t cc.Type) string {
+	var npad int
+	b := bytesBufferPool.Get().(*bytes.Buffer)
+	defer func() { b.Reset(); bytesBufferPool.Put(b) }()
+	switch t.Kind() {
+	case cc.Struct:
+		info := cc.NewStructLayout(t)
+		// trc("%v: %q\n%s", p.pos(n), t.Tag(), info)
+		b.WriteString("struct {")
+		if info.NeedExplicitAlign {
+			fmt.Fprintf(b, "%s [0]uint%d;", p.padName(&npad), 8*p.align(n, t))
+		}
+		var max uintptr
+		for _, off := range info.Offsets {
+			flds := info.OffsetToFields[off]
+			if off < max {
+				var a []string
+				var nmf cc.Field
+				for _, f := range flds {
+					if f.Name() != 0 && nmf == nil {
+						nmf = f
+					}
+					if !f.IsBitField() {
+						panic(todo("internal error %q, off %v max %v\n%s", f.Name(), off, max, info))
+					}
+					a = append(a, fmt.Sprintf("%s %s: %d", f.Type(), f.Name(), f.BitFieldWidth()))
+				}
+				fmt.Fprintf(b, "/* %s */", strings.Join(a, ", "))
+				continue
+			}
+
+			f := flds[0]
+			switch pad := info.PaddingsBefore[f]; {
+			case pad < 0:
+				continue
+			case pad > 0:
+				fmt.Fprintf(b, "%s [%d]byte;", p.padName(&npad), pad)
+			}
+			switch {
+			case f.IsBitField():
+				max += uintptr(f.BitFieldBlockWidth()) >> 3
+				var a []string
+				var nmf cc.Field
+				for _, f := range flds {
+					if f.Name() != 0 && nmf == nil {
+						nmf = f
+					}
+					if !f.IsBitField() {
+						panic(todo("internal error %q\n%s", f.Name(), info))
+					}
+					a = append(a, fmt.Sprintf("%s %s: %d", f.Type(), f.Name(), f.BitFieldWidth()))
+				}
+				if nmf == nil {
+					nmf = f
+				}
+				fmt.Fprintf(b, "%s uint%d /* %s */;", p.bitFieldName(n, nmf), f.BitFieldBlockWidth(), strings.Join(a, ", "))
+			default:
+				ft := f.Type()
+				if ft.Kind() == cc.Array && ft.IsIncomplete() || ft.Size() == 0 {
+					break
+				}
+
+				max += ft.Size()
+				fmt.Fprintf(b, "%s %s;", p.fieldName2(n, f), p.typ(n, ft))
+			}
+		}
+		if info.PaddingAfter != 0 {
+			fmt.Fprintf(b, "%s [%d]byte;", p.padName(&npad), info.PaddingAfter)
+		}
+		b.WriteByte('}')
+	case cc.Union:
+		b.WriteString("struct {")
+		info := cc.NewStructLayout(t)
+		if info.NeedExplicitAlign {
+			fmt.Fprintf(b, "%s [0]uint%d;", p.padName(&npad), 8*p.align(n, t))
+		}
+		al := uintptr(t.Align())
+		sz := t.Size()
+		if al > sz {
+			panic(todo("", p.pos(n)))
+		}
+
+		f := t.FieldByIndex([]int{0})
+		ft := f.Type()
+		al0 := ft.Align()
+		if f.IsBitField() {
+			al0 = f.BitFieldBlockWidth() >> 3
+		}
+		if al != uintptr(al0) {
+			fmt.Fprintf(b, "%s [0]uint%d;", p.padName(&npad), 8*al)
+		}
+		fsz := ft.Size()
+		switch {
+		case f.IsBitField():
+			fmt.Fprintf(b, "%s ", p.fieldName2(n, f))
+			fmt.Fprintf(b, "uint%d;", f.BitFieldBlockWidth())
+			fsz = uintptr(f.BitFieldBlockWidth()) >> 3
+		default:
+			fmt.Fprintf(b, "%s %s;", p.fieldName2(n, f), p.typ(n, ft))
+		}
+		if pad := sz - fsz; pad != 0 {
+			fmt.Fprintf(b, "%s [%d]byte;", p.padName(&npad), pad)
+		}
+		b.WriteByte('}')
+	default:
+		panic(todo("internal error: %v", t.Kind()))
+	}
+	r := b.String()
+	if p.task.verifyStructs {
+		if _, ok := p.verifyStructs[r]; !ok {
+			p.verifyStructs[r] = t
+		}
+	}
+	return r
+}
+
+func (p *project) align(nd cc.Node, t cc.Type) int {
+	switch n := t.Align(); {
+	case n <= 1:
+		return 1
+	case n <= 2:
+		return 2
+	case n <= 4:
+		return 4
+	case n <= 8:
+		return 8
+	default:
+		if !p.task.ignoreUnsupportedAligment {
+			p.err(nd, "unsupported alignment of type %s: %v", t, n)
+		}
+		return 8
+	}
+}
+
+func (p *project) bitFieldName(n cc.Node, f cc.Field) string {
+	if id := f.Name(); id != 0 {
+		return p.fieldName(n, id)
+	}
+
+	return fmt.Sprintf("__%d", f.Offset())
+}
+
+func (p *project) fieldName2(n cc.Node, f cc.Field) string {
+	if f.Name() != 0 {
+		return p.fieldName(n, f.Name())
+	}
+
+	return p.fieldName(n, cc.String(fmt.Sprintf("__%d", f.Offset())))
+}
+
+func (p *project) fieldName(n cc.Node, id cc.StringID) string {
+	if id == 0 {
+		panic(todo("", p.pos(n)))
+	}
+
+	if !p.task.exportFieldsValid {
+		s := id.String()
+		if p.task.defaultUnExport {
+			s = unCapitalize(s)
+		}
+
+		if !reservedNames[s] {
+			return s
+		}
+
+		return "__" + s
+	}
+
+	if s := p.task.exportFields; s != "" {
+		return s + id.String()
+	}
+
+	return capitalize(id.String())
+}
+
+func (p *project) dtyp(d *cc.Declarator) (r string) {
+	t := d.Type()
+	if t.IsIncomplete() {
+		if t.Kind() == cc.Array && d.IsParameter {
+			return "uintptr"
+		}
+
+		panic(todo(""))
+	}
+
+	return p.typ(d, t)
+}
+
+func (p *project) typ(nd cc.Node, t cc.Type) (r string) {
+	if t.IsIncomplete() {
+		panic(todo("", p.pos(nd), t))
+	}
+
+	if t.IsAliasType() {
+		if tld := p.tlds[t.AliasDeclarator()]; tld != nil {
+			return tld.name
+		}
+	}
+
+	b := bytesBufferPool.Get().(*bytes.Buffer)
+	defer func() { b.Reset(); bytesBufferPool.Put(b) }()
+	if t.IsIntegerType() {
+		switch t.Kind() {
+		case cc.Int128:
+			fmt.Fprintf(b, "%sInt128", p.task.crt)
+			return b.String()
+		case cc.UInt128:
+			fmt.Fprintf(b, "%sUint128", p.task.crt)
+			return b.String()
+		}
+
+		if !t.IsSignedType() {
+			b.WriteByte('u')
+		}
+		if t.Size() > 8 {
+			p.err(nd, "unsupported C type: %v", t)
+		}
+		fmt.Fprintf(b, "int%d", 8*t.Size())
+		return b.String()
+	}
+
+	switch t.Kind() {
+	case cc.Ptr, cc.Function:
+		return "uintptr"
+	case cc.Double:
+		return "float64"
+	case cc.Float:
+		return "float32"
+	case cc.Array:
+		n := t.Len()
+		switch {
+		case t.IsVLA():
+			fmt.Fprintf(b, "uintptr")
+		default:
+			fmt.Fprintf(b, "[%d]%s", n, p.typ(nd, t.Elem()))
+		}
+		return b.String()
+	case cc.Vector:
+		n := t.Len()
+		fmt.Fprintf(b, "[%d]%s", n, p.typ(nd, t.Elem()))
+		return b.String()
+	case cc.Struct, cc.Union:
+		if tag := t.Tag(); tag != 0 {
+			if s := p.structs[tag]; s != nil {
+				if s.name == "" {
+					panic(todo("internal error %q", tag))
+				}
+
+				return s.name
+			}
+		}
+
+		return p.structType(nd, t)
+	}
+
+	panic(todo("", p.pos(nd), t.Kind(), t))
+}
+
+func isScalarKind(k cc.Kind) bool {
+	switch k {
+	case
+		cc.Char, cc.SChar, cc.UChar,
+		cc.Short, cc.UShort,
+		cc.Int, cc.UInt,
+		cc.Long, cc.ULong,
+		cc.LongLong, cc.ULongLong,
+		cc.Float, cc.Double,
+		cc.Ptr:
+
+		return true
+	}
+
+	return false
+}
+
+func (p *project) layoutTLDs() error {
+	var t0 time.Time
+	if p.task.traceTranslationUnits {
+		fmt.Printf("processing file scope declarations ... ")
+		t0 = time.Now()
+		defer func() { fmt.Println(time.Since(t0)) }()
+	}
+
+	exportExtern, exportTypedef := doNotChange, doNotChange
+	if p.task.exportExternsValid {
+		switch {
+		case p.task.exportExterns != "":
+			exportExtern = exportPrefix
+		default:
+			exportExtern = exportCapitalize
+		}
+	} else if p.task.defaultUnExport {
+		exportExtern = doNotExport
+	}
+
+	if p.task.exportTypedefsValid {
+		switch {
+		case p.task.exportTypedefs != "":
+			exportTypedef = exportPrefix
+		default:
+			exportTypedef = exportCapitalize
+		}
+	} else if p.task.defaultUnExport {
+		exportTypedef = doNotExport
+	}
+
+	var a []*cc.Declarator
+	if p.task.pkgName == "" || p.task.pkgName == "main" {
+	out:
+		for _, ast := range p.task.asts {
+			if a := ast.Scope[idMain]; len(a) != 0 {
+				switch x := a[0].(type) {
+				case *cc.Declarator:
+					if x.Linkage == cc.External {
+						p.isMain = true
+						p.scope.take(idMain)
+						break out
+					}
+				}
+			}
+		}
+	}
+	sharedFns := map[*cc.FunctionDefinition]struct{}{}
+	for _, ast := range p.task.asts {
+		a = a[:0]
+		for d := range ast.TLD {
+			if d.IsFunctionPrototype() {
+				continue
+			}
+
+			// https://gcc.gnu.org/onlinedocs/gcc/Inline.html
+			//
+			// If you specify both inline and extern in the function definition, then the
+			// definition is used only for inlining. In no case is the function compiled on
+			// its own, not even if you refer to its address explicitly. Such an address
+			// becomes an external reference, as if you had only declared the function, and
+			// had not defined it.
+			//
+			// This combination of inline and extern has almost the effect of a macro. The
+			// way to use it is to put a function definition in a header file with these
+			// keywords, and put another copy of the definition (lacking inline and extern)
+			// in a library file. The definition in the header file causes most calls to
+			// the function to be inlined. If any uses of the function remain, they refer
+			// to the single copy in the library.
+			if d.IsExtern() && d.Type().Inline() {
+				continue
+			}
+
+			if fn := d.FunctionDefinition(); fn != nil {
+				if _, ok := p.sharedFns[fn]; ok {
+					if _, ok := sharedFns[fn]; ok {
+						continue
+					}
+
+					sharedFns[fn] = struct{}{}
+				}
+			}
+
+			a = append(a, d)
+			p.wanted[d] = struct{}{}
+		}
+		sort.Slice(a, func(i, j int) bool {
+			return a[i].NameTok().Seq() < a[j].NameTok().Seq()
+		})
+		for _, d := range a {
+			switch d.Type().Kind() {
+			case cc.Struct, cc.Union:
+				p.checkAttributes(d.Type())
+			}
+			nm := d.Name()
+			name := nm.String()
+
+			switch d.Linkage {
+			case cc.External:
+				if ex := p.externs[nm]; ex != nil {
+					if _, ok := p.task.hide[name]; ok {
+						break
+					}
+
+					if d.Type().Kind() != cc.Function {
+						break
+					}
+
+					p.err(d, "redeclared: %s", d.Name())
+					break
+				}
+
+				isMain := p.isMain && nm == idMain
+				switch exportExtern {
+				case doNotExport:
+					name = unCapitalize(name)
+				case doNotChange:
+					// nop
+				case exportCapitalize:
+					name = capitalize(name)
+				case exportPrefix:
+					name = p.task.exportExterns + name
+				}
+				name = p.scope.take(cc.String(name))
+				if isMain {
+					p.mainName = name
+					d.Read++
+				}
+				tld := &tld{name: name}
+				p.externs[nm] = tld
+				for _, v := range ast.Scope[nm] {
+					if d, ok := v.(*cc.Declarator); ok {
+						p.tlds[d] = tld
+					}
+				}
+				if !isMain {
+					p.capi = append(p.capi, d.Name().String())
+				}
+			case cc.Internal:
+				if token.IsExported(name) && !p.isMain && p.task.exportExternsValid {
+					name = "s" + name
+				}
+				tld := &tld{name: p.scope.take(cc.String(name))}
+				for _, v := range ast.Scope[nm] {
+					if d, ok := v.(*cc.Declarator); ok {
+						p.tlds[d] = tld
+					}
+				}
+			case cc.None:
+				if d.IsTypedefName {
+					if d.Type().IsIncomplete() {
+						break
+					}
+
+					if exportTypedef == doNotChange && strings.HasPrefix(name, "__") {
+						break
+					}
+
+					ex, ok := p.typedefTypes[d.Name()]
+					if ok {
+						sig := p.typeSignature(d, d.Type())
+						if ex.sig == sig {
+							tld := ex.tld
+							for _, v := range ast.Scope[nm] {
+								if d, ok := v.(*cc.Declarator); ok {
+									p.tlds[d] = tld
+								}
+							}
+							break
+						}
+					}
+
+					switch exportTypedef {
+					case doNotExport:
+						name = unCapitalize(name)
+					case doNotChange:
+						// nop
+					case exportCapitalize:
+						name = capitalize(name)
+					case exportPrefix:
+						name = p.task.exportTypedefs + name
+					}
+
+					tld := &tld{name: p.scope.take(cc.String(name))}
+					p.typedefTypes[d.Name()] = &typedef{p.typeSignature(d, d.Type()), tld}
+					for _, v := range ast.Scope[nm] {
+						if d, ok := v.(*cc.Declarator); ok {
+							p.tlds[d] = tld
+						}
+					}
+				}
+			default:
+				panic(todo("", p.pos(d), nm, d.Linkage))
+			}
+		}
+	}
+	for _, ast := range p.task.asts {
+		for list := ast.TranslationUnit; list != nil; list = list.TranslationUnit {
+			decl := list.ExternalDeclaration
+			switch decl.Case {
+			case cc.ExternalDeclarationFuncDef: // FunctionDefinition
+				// ok
+			default:
+				continue
+			}
+
+			cc.Inspect(decl.FunctionDefinition.CompoundStatement, func(n cc.Node, entry bool) bool {
+				switch x := n.(type) {
+				case *cc.Declarator:
+					if x.IsFunctionPrototype() {
+						nm := x.Name()
+						if extern := p.externs[nm]; extern != nil {
+							break
+						}
+
+						tld := &tld{name: nm.String()}
+						for _, nd := range ast.Scope[nm] {
+							if d, ok := nd.(*cc.Declarator); ok {
+								p.tlds[d] = tld
+							}
+						}
+					}
+
+				}
+				return true
+			})
+		}
+	}
+	return nil
+}
+
+func (p *project) checkAttributes(t cc.Type) (r bool) {
+	r = true
+	for _, v := range t.Attributes() {
+		cc.Inspect(v, func(n cc.Node, entry bool) bool {
+			if !entry {
+				return true
+			}
+
+			switch x := n.(type) {
+			case *cc.AttributeValue:
+				if x.Token.Value != idAligned {
+					break
+				}
+
+				//TODO switch v := x.ExpressionList.AssignmentExpression.Operand.Value().(type) {
+				//TODO default:
+				//TODO 	panic(todo("%T(%v)", v, v))
+				//TODO }
+			}
+			return true
+		})
+	}
+	switch t.Kind() {
+	case cc.Struct, cc.Union:
+		for i := []int{0}; i[0] < t.NumField(); i[0]++ {
+			f := t.FieldByIndex(i)
+			if !p.checkAttributes(f.Type()) {
+				return false
+			}
+
+			sd := f.Declarator()
+			if sd == nil {
+				continue
+			}
+
+			cc.Inspect(sd.StructDeclaration().SpecifierQualifierList, func(n cc.Node, entry bool) bool {
+				if !entry {
+					return true
+				}
+
+				switch x := n.(type) {
+				case *cc.AttributeValue:
+					if x.Token.Value == idPacked {
+						p.err(sd, "unsupported attribute: packed")
+						r = false
+						return false
+					}
+
+					if x.Token.Value != idAligned {
+						break
+					}
+
+					switch v := x.ExpressionList.AssignmentExpression.Operand.Value().(type) {
+					case cc.Int64Value:
+						if int(v) != t.Align() {
+							p.err(sd, "unsupported attribute: alignment")
+							r = false
+							return false
+						}
+					default:
+						panic(todo("%T(%v)", v, v))
+					}
+				}
+				return true
+			})
+			if !r {
+				return false
+			}
+		}
+	}
+	return r
+}
+
+func unCapitalize(s string) string {
+	if strings.HasPrefix(s, "_") {
+		return s
+	}
+	a := []rune(s)
+	return strings.ToLower(string(a[0])) + string(a[1:])
+}
+
+func capitalize(s string) string {
+	if strings.HasPrefix(s, "_") {
+		s = "X" + s
+	}
+	a := []rune(s)
+	return strings.ToUpper(string(a[0])) + string(a[1:])
+}
+
+func (p *project) main() error {
+	targs := append([]string(nil), p.task.args...)
+	for i, v := range targs {
+		if v == "" {
+			targs[i] = `""`
+		}
+	}
+	p.o(`// Code generated by '%s %s', DO NOT EDIT.
+
+package %s
+
+`,
+		filepath.Base(p.task.args[0]),
+		strings.Join(targs[1:], " "),
+		p.task.pkgName,
+	)
+	if len(p.defineLines) != 0 {
+		p.w("\nconst (")
+		p.w("%s", strings.Join(p.defineLines, "\n"))
+		p.w("\n)\n\n")
+	}
+	var a []*enumSpec
+	for _, es := range p.enumSpecs {
+		if es.spec.LexicalScope().Parent() == nil && !es.emitted {
+			a = append(a, es)
+		}
+	}
+	sort.Slice(a, func(i, j int) bool {
+		return a[i].decl.Position().String() < a[j].decl.Position().String()
+	})
+	for _, es := range a {
+		es.emit(p)
+	}
+	for i, v := range p.task.asts {
+		var t0 time.Time
+		if p.task.traceTranslationUnits {
+			fmt.Printf("Go back end %v/%v: %s ... ", i+1, len(p.task.asts), filepath.Base(p.task.sources[i].Name))
+			t0 = time.Now()
+		}
+		p.oneAST(v)
+		if p.task.traceTranslationUnits {
+			fmt.Println(time.Since(t0))
+		}
+		p.task.asts[i] = nil
+		memGuard(i, p.task.isScripted)
+	}
+	sort.Slice(p.task.imported, func(i, j int) bool { return p.task.imported[i].path < p.task.imported[j].path })
+	p.o(`import (
+	"math"
+	"reflect"
+	"sync/atomic"
+	"unsafe"
+`)
+	if len(p.verifyStructs) != 0 {
+		p.o("\t\"fmt\"\n")
+	}
+	first := true
+	libc := false
+	for _, v := range p.task.imported {
+		if v.used {
+			if v.path == p.task.crtImportPath {
+				libc = true
+			}
+			if first {
+				p.o("\n")
+				first = false
+			}
+			p.o("\t%q\n", v.path)
+		}
+	}
+	if p.task.crtImportPath != "" {
+		if !libc {
+			p.o("\t%q\n", p.task.crtImportPath)
+		}
+		p.o("\t%q\n", p.task.crtImportPath+"/sys/types")
+	}
+	p.o(`)
+
+var _ = math.Pi
+var _ reflect.Kind
+var _ atomic.Value
+var _ unsafe.Pointer
+`)
+	if p.task.crtImportPath != "" {
+		if libc {
+			p.o("var _ *libc.TLS\n")
+		}
+		p.o("var _ types.Size_t\n")
+	}
+	if p.isMain {
+		p.o(`
+func main() { %sStart(%s) }`, p.task.crt, p.mainName)
+	}
+	p.flushStructs()
+	p.initPatches()
+	p.flushTS()
+	if !p.task.noCapi {
+		p.flushCAPI()
+	}
+	p.doVerifyStructs()
+	if err := p.Err(); err != nil {
+		return err
+	}
+
+	if _, err := p.buf.WriteTo(p.task.out); err != nil {
+		return err
+	}
+
+	return p.Err()
+}
+
+func (p *project) doVerifyStructs() {
+	if len(p.verifyStructs) == 0 {
+		return
+	}
+
+	var a []string
+	for k := range p.verifyStructs {
+		a = append(a, k)
+	}
+	sort.Strings(a)
+	p.w("\n\nfunc init() {")
+	n := 0
+	for _, k := range a {
+		t := p.verifyStructs[k]
+		p.w("\nvar v%d %s", n, k)
+		p.w("\nif g, e := unsafe.Sizeof(v%d), uintptr(%d); g != e { panic(fmt.Sprintf(`invalid struct/union size, got %%v, expected %%v`, g, e))}", n, t.Size())
+		nf := t.NumField()
+		for idx := []int{0}; idx[0] < nf; idx[0]++ {
+			f := t.FieldByIndex(idx)
+			if f.IsFlexible() {
+				break
+			}
+
+			if f.IsBitField() || f.Type().Size() == 0 {
+				continue
+			}
+
+			nm := p.fieldName2(f.Declarator(), f)
+			switch {
+			case t.Kind() == cc.Union:
+				if f.Offset() != 0 {
+					panic(todo(""))
+				}
+
+				if idx[0] != 0 {
+					break
+				}
+
+				fallthrough
+			default:
+				p.w("\nif g, e := unsafe.Offsetof(v%d.%s), uintptr(%d); g != e { panic(fmt.Sprintf(`invalid struct/union field offset, got %%v, expected %%v`, g, e))}", n, nm, f.Offset())
+				p.w("\nif g, e := unsafe.Sizeof(v%d.%s), uintptr(%d); g != e { panic(fmt.Sprintf(`invalid struct/union field size, got %%v, expected %%v`, g, e))}", n, nm, f.Type().Size())
+			}
+		}
+		n++
+	}
+	p.w("\n}\n")
+}
+
+func (p *project) flushCAPI() {
+	if p.isMain {
+		return
+	}
+
+	b := bytes.NewBuffer(nil)
+	fmt.Fprintf(b, `// Code generated by '%s %s', DO NOT EDIT.
+
+package %s
+
+`,
+		filepath.Base(p.task.args[0]),
+		strings.Join(p.task.args[1:], " "),
+		p.task.pkgName,
+	)
+	fmt.Fprintf(b, "\n\nvar CAPI = map[string]struct{}{")
+	sort.Strings(p.capi)
+	for _, nm := range p.capi {
+		fmt.Fprintf(b, "\n%q: {},", nm)
+	}
+	fmt.Fprintf(b, "\n}\n")
+	if err := ioutil.WriteFile(p.task.capif, b.Bytes(), 0644); err != nil {
+		p.err(nil, "%v", err)
+		return
+	}
+
+	if out, err := exec.Command("gofmt", "-r", "(x) -> x", "-l", "-s", "-w", p.task.capif).CombinedOutput(); err != nil {
+		p.err(nil, "%s: %v", out, err)
+	}
+
+	if out, err := exec.Command("gofmt", "-l", "-s", "-w", p.task.capif).CombinedOutput(); err != nil {
+		p.err(nil, "%s: %v", out, err)
+	}
+}
+
+func (p *project) initPatches() {
+	var tlds []*tld
+	for _, tld := range p.tlds {
+		if len(tld.patches) != 0 {
+			tlds = append(tlds, tld)
+		}
+	}
+	if len(tlds) == 0 {
+		return
+	}
+
+	sort.Slice(tlds, func(i, j int) bool { return tlds[i].name < tlds[j].name })
+	p.w("\n\nfunc init() {")
+	for _, tld := range tlds {
+		for _, patch := range tld.patches {
+			var fld string
+			if patch.fld != nil {
+				fld = fmt.Sprintf("/* .%s */", patch.fld.Name())
+			}
+			init := patch.init
+			expr := init.AssignmentExpression
+			d := expr.Declarator()
+			switch {
+			case d != nil && d.Type().Kind() == cc.Function:
+				p.w("\n*(*")
+				p.functionSignature(d, nil, d.Type(), "")
+				p.w(")(unsafe.Pointer(uintptr(unsafe.Pointer(&%s))+%d%s)) = ", tld.name, init.Offset, fld)
+				p.declarator(init, nil, d, d.Type(), exprFunc, 0)
+			default:
+				p.w("\n*(*%s)(unsafe.Pointer(uintptr(unsafe.Pointer(&%s))+%d%s)) = ", p.typ(init, patch.t), tld.name, init.Offset, fld)
+				p.assignmentExpression(nil, expr, patch.t, exprValue, 0)
+			}
+			p.w("// %s:", p.pos(init))
+		}
+	}
+	p.w("\n}\n")
+}
+
+func (p *project) Err() error {
+	if len(p.errors) == 0 {
+		return nil
+	}
+
+	var lpos token.Position
+	w := 0
+	for _, v := range p.errors {
+		if lpos.Filename != "" {
+			if v.Pos.Filename == lpos.Filename && v.Pos.Line == lpos.Line && !strings.HasPrefix(v.Msg, tooManyErrors) {
+				continue
+			}
+		}
+
+		p.errors[w] = v
+		w++
+		lpos = v.Pos
+	}
+	p.errors = p.errors[:w]
+	sort.Slice(p.errors, func(i, j int) bool {
+		a := p.errors[i]
+		if a.Msg == tooManyErrors {
+			return false
+		}
+
+		b := p.errors[j]
+		if b.Msg == tooManyErrors {
+			return true
+		}
+
+		if !a.Pos.IsValid() && b.Pos.IsValid() {
+			return true
+		}
+
+		if a.Pos.IsValid() && !b.Pos.IsValid() {
+			return false
+		}
+
+		if a.Pos.Filename < b.Pos.Filename {
+			return true
+		}
+
+		if a.Pos.Filename > b.Pos.Filename {
+			return false
+		}
+
+		if a.Pos.Line < b.Pos.Line {
+			return true
+		}
+
+		if a.Pos.Line > b.Pos.Line {
+			return false
+		}
+
+		return a.Pos.Column < b.Pos.Column
+	})
+	a := make([]string, 0, len(p.errors))
+	for _, v := range p.errors {
+		a = append(a, v.Error())
+	}
+	return fmt.Errorf("%s", strings.Join(a, "\n"))
+}
+
+func (p *project) flushTS() {
+	b := p.ts.Bytes()
+	if len(b) != 0 {
+		p.w("\n\n")
+		//TODO add cmd line option for this
+		//TODO s := strings.TrimSpace(hex.Dump(b))
+		//TODO a := strings.Split(s, "\n")
+		//TODO p.w("//  %s\n", strings.Join(a, "\n//  "))
+		p.w("var %s = %q\n", p.tsName, b)
+		p.w("var %s = (*reflect.StringHeader)(unsafe.Pointer(&%s)).Data\n", p.tsNameP, p.tsName)
+	}
+	if len(p.tsW) != 0 {
+		p.w("var %s = [...]%s{", p.tsWName, p.typ(nil, p.ast.WideCharType))
+		for _, v := range p.tsW {
+			p.w("%d, ", v)
+		}
+		p.w("}\n")
+		p.w("var %s = uintptr(unsafe.Pointer(&%s[0]))\n", p.tsWNameP, p.tsWName)
+	}
+}
+
+func (p *project) flushStructs() {
+	var a []*taggedStruct
+	for _, v := range p.structs {
+		if !v.emitted {
+			a = append(a, v)
+		}
+	}
+	sort.Slice(a, func(i, j int) bool { return a[i].name < a[j].name })
+	for _, v := range a {
+		v.emit(p, nil)
+	}
+}
+
+func (p *project) oneAST(ast *cc.AST) {
+	p.ast = ast
+	for list := ast.TranslationUnit; list != nil; list = list.TranslationUnit {
+		p.externalDeclaration(list.ExternalDeclaration)
+	}
+	p.w("%s", tidyCommentString(ast.TrailingSeperator.String()))
+}
+
+func (p *project) externalDeclaration(n *cc.ExternalDeclaration) {
+	switch n.Case {
+	case cc.ExternalDeclarationFuncDef: // FunctionDefinition
+		p.functionDefinition(n.FunctionDefinition)
+	case cc.ExternalDeclarationDecl: // Declaration
+		p.declaration(nil, n.Declaration, false)
+	case cc.ExternalDeclarationAsm: // AsmFunctionDefinition
+		// nop
+	case cc.ExternalDeclarationAsmStmt: // AsmStatement
+		panic(todo("", p.pos(n)))
+	case cc.ExternalDeclarationEmpty: // ';'
+		// nop
+	case cc.ExternalDeclarationPragma: // PragmaSTDC
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) declaration(f *function, n *cc.Declaration, topDecl bool) {
+	cc.Inspect(n.DeclarationSpecifiers, func(m cc.Node, entry bool) bool {
+		switch x := m.(type) {
+		case *cc.EnumSpecifier:
+			if f == nil {
+				p.enumSpecs[x].emit(p)
+			}
+		case *cc.StructOrUnionSpecifier:
+			if tag := x.Token.Value; tag != 0 {
+				switch {
+				case f == nil:
+					p.structs[tag].emit(p, n.DeclarationSpecifiers)
+				default:
+					p.localTaggedStructs = append(p.localTaggedStructs, func() {
+						p.structs[tag].emit(p, n.DeclarationSpecifiers)
+					})
+				}
+			}
+		}
+		return true
+	})
+
+	if n.InitDeclaratorList == nil {
+		return
+	}
+
+	// DeclarationSpecifiers InitDeclaratorList ';'
+	sep := tidyComment("\n", n) //TODO repeats
+	for list := n.InitDeclaratorList; list != nil; list = list.InitDeclaratorList {
+		p.initDeclarator(f, list.InitDeclarator, sep, topDecl)
+		sep = "\n"
+	}
+}
+
+func (p *project) initDeclarator(f *function, n *cc.InitDeclarator, sep string, topDecl bool) {
+	if f == nil {
+		p.tld(f, n, sep, false)
+		return
+	}
+
+	d := n.Declarator
+	if d.IsExtern() || d.IsTypedefName {
+		return
+	}
+
+	if tld := p.tlds[d]; tld != nil && !topDecl { // static local
+		if !p.pass1 {
+			p.staticQueue = append(p.staticQueue, n)
+		}
+		return
+	}
+
+	local := f.locals[d]
+	if local == nil { // Dead declaration.
+		return
+	}
+
+	block := f.block
+	t := d.Type()
+	vla := t.Kind() == cc.Array && t.IsVLA()
+	if vla && p.pass1 {
+		f.vlas[d] = struct{}{}
+		return
+	}
+
+	switch n.Case {
+	case cc.InitDeclaratorDecl: // Declarator AttributeSpecifierList
+		if block.noDecl || block.topDecl && !topDecl {
+			return
+		}
+
+		switch {
+		case vla:
+			p.initDeclaratorDeclVLA(f, n, sep)
+		default:
+			p.initDeclaratorDecl(f, n, sep)
+		}
+	case cc.InitDeclaratorInit: // Declarator AttributeSpecifierList '=' Initializer
+		if vla {
+			panic(todo(""))
+		}
+
+		if f.block.topDecl {
+			switch {
+			case topDecl:
+				p.initDeclaratorDecl(f, n, sep)
+				if local.forceRead && !local.isPinned {
+					p.w("_ = %s;", local.name)
+				}
+			default:
+				sv := f.condInitPrefix
+				f.condInitPrefix = func() {
+					p.declarator(d, f, d, d.Type(), exprLValue, 0)
+					p.w(" = ")
+				}
+				switch {
+				case p.isConditionalInitializer(n.Initializer):
+					p.assignmentExpression(f, n.Initializer.AssignmentExpression, d.Type(), exprCondInit, 0)
+				default:
+					f.condInitPrefix()
+					p.initializer(f, n.Initializer, d.Type(), d.StorageClass, nil)
+				}
+				f.condInitPrefix = sv
+				p.w(";")
+			}
+			return
+		}
+
+		p.w("%s", sep)
+		switch {
+		case local.isPinned:
+			sv := f.condInitPrefix
+			f.condInitPrefix = func() {
+				//TODO- p.declarator(d, f, d, d.Type(), exprLValue, 0)
+				//TODO- p.w(" = ")
+				p.w("*(*%s)(unsafe.Pointer(%s%s/* %s */)) = ", p.typ(n, d.Type()), f.bpName, nonZeroUintptr(local.off), local.name)
+			}
+			switch {
+			case p.isConditionalInitializer(n.Initializer):
+				p.assignmentExpression(f, n.Initializer.AssignmentExpression, d.Type(), exprCondInit, 0)
+			default:
+				f.condInitPrefix()
+				p.initializer(f, n.Initializer, d.Type(), d.StorageClass, nil)
+				p.w(";")
+			}
+			f.condInitPrefix = sv
+			p.w(";")
+		default:
+			var semi string
+			switch {
+			case block.noDecl:
+				semi = ""
+			default:
+				p.w("var %s ", local.name)
+				if !isAggregateTypeOrUnion(d.Type()) {
+					p.w("%s ", p.typ(n, d.Type()))
+				}
+				semi = ";"
+			}
+			switch {
+			case p.isConditionalInitializer(n.Initializer):
+				p.w("%s", semi)
+				sv := f.condInitPrefix
+				f.condInitPrefix = func() { p.w("%s = ", local.name) }
+				p.assignmentExpression(f, n.Initializer.AssignmentExpression, d.Type(), exprCondInit, 0)
+				f.condInitPrefix = sv
+			default:
+				if block.noDecl {
+					p.w("%s", local.name)
+				}
+				p.w(" = ")
+				p.initializer(f, n.Initializer, d.Type(), d.StorageClass, nil)
+			}
+			p.w(";")
+		}
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+	if !block.noDecl && local.forceRead && !local.isPinned {
+		p.w("_ = %s;", local.name)
+	}
+}
+
+func (p *project) isConditionalInitializer(n *cc.Initializer) bool {
+	return n.Case == cc.InitializerExpr && p.isConditionalAssignmentExpr(n.AssignmentExpression)
+}
+
+func (p *project) isConditionalAssignmentExpr(n *cc.AssignmentExpression) bool {
+	return n.Case == cc.AssignmentExpressionCond &&
+		n.ConditionalExpression.Case == cc.ConditionalExpressionCond
+}
+
+func (p *project) initDeclaratorDeclVLA(f *function, n *cc.InitDeclarator, sep string) {
+	d := n.Declarator
+	local := f.locals[d]
+	if strings.TrimSpace(sep) == "" {
+		sep = "\n"
+	}
+	if local.isPinned {
+		panic(todo(""))
+		p.w("%s// var %s %s at %s%s, %d\n", sep, local.name, p.typ(n, d.Type()), f.bpName, nonZeroUintptr(local.off), d.Type().Size())
+		return
+	}
+
+	p.w("%s%s = %sXrealloc(%s, %s, types.Size_t(", sep, local.name, p.task.crt, f.tlsName, local.name)
+	e := d.Type().LenExpr()
+	p.assignmentExpression(f, e, e.Operand.Type(), exprValue, 0)
+	if sz := d.Type().Elem().Size(); sz != 1 {
+		p.w("*%d", sz)
+	}
+	p.w("));")
+}
+
+func (p *project) initDeclaratorDecl(f *function, n *cc.InitDeclarator, sep string) {
+	d := n.Declarator
+	local := f.locals[d]
+	if strings.TrimSpace(sep) == "" {
+		sep = "\n"
+	}
+	if local.isPinned {
+		p.w("%s// var %s %s at %s%s, %d\n", sep, local.name, p.typ(n, d.Type()), f.bpName, nonZeroUintptr(local.off), d.Type().Size())
+		return
+	}
+
+	p.w("%svar %s %s;", sep, local.name, p.typ(n, d.Type()))
+}
+
+func (p *project) declarator(n cc.Node, f *function, d *cc.Declarator, t cc.Type, mode exprMode, flags flags) {
+	switch mode {
+	case exprLValue:
+		p.declaratorLValue(n, f, d, t, mode, flags)
+	case exprFunc:
+		p.declaratorFunc(n, f, d, t, mode, flags)
+	case exprValue:
+		p.declaratorValue(n, f, d, t, mode, flags)
+	case exprAddrOf:
+		p.declaratorAddrOf(n, f, d, t, flags)
+	case exprSelect:
+		p.declaratorSelect(n, f, d)
+	case exprDecay:
+		p.declaratorDecay(n, f, d, t, mode, flags)
+	default:
+		panic(todo("", mode))
+	}
+}
+
+func (p *project) declaratorDecay(n cc.Node, f *function, d *cc.Declarator, t cc.Type, mode exprMode, flags flags) {
+	if d.Type().Kind() != cc.Array {
+		panic(todo("", n.Position(), p.pos(d)))
+	}
+
+	if f != nil {
+		if local := f.locals[d]; local != nil {
+			if d.Type().IsVLA() {
+				switch {
+				case local.isPinned:
+					panic(todo(""))
+				default:
+					p.w("%s", local.name)
+					return
+				}
+			}
+
+			if d.IsParameter {
+				p.w("%s", local.name)
+				return
+			}
+
+			if p.pass1 {
+				if !d.Type().IsVLA() {
+					f.pin(n, d)
+				}
+				return
+			}
+
+			p.w("(%s%s)/* &%s[0] */", f.bpName, nonZeroUintptr(local.off), local.name)
+			return
+		}
+	}
+
+	if x := p.tlds[d]; x != nil && d.IsStatic() {
+		p.w("uintptr(unsafe.Pointer(&%s))", x.name)
+		return
+	}
+
+	switch x := p.symtab[d.Name().String()].(type) {
+	case *tld:
+		p.w("uintptr(unsafe.Pointer(&%s))", x.name)
+	case *imported:
+		x.used = true
+		p.w("uintptr(unsafe.Pointer(&%sX%s))", x.qualifier, d.Name())
+	default:
+		panic(todo("%v: %v: %q %T", n.Position(), p.pos(d), d.Name(), x))
+	}
+}
+
+func (p *project) declaratorValue(n cc.Node, f *function, d *cc.Declarator, t cc.Type, mode exprMode, flags flags) {
+	switch k := p.declaratorKind(d); k {
+	case opNormal:
+		p.declaratorValueNormal(n, f, d, t, mode, flags)
+	case opArray:
+		p.declaratorValueArray(n, f, d, t, mode, flags)
+	case opFunction:
+		p.declarator(n, f, d, t, exprAddrOf, flags)
+	case opUnion:
+		p.declaratorValueUnion(n, f, d, t, mode, flags)
+	case opArrayParameter:
+		p.declaratorValueArrayParameter(n, f, d, t, mode, flags)
+	default:
+		panic(todo("", d.Position(), k))
+	}
+}
+
+func (p *project) declaratorValueArrayParameter(n cc.Node, f *function, d *cc.Declarator, t cc.Type, mode exprMode, flags flags) {
+	if d.Type().IsScalarType() {
+		defer p.w("%s", p.convertType(n, d.Type(), t, flags))
+	}
+	local := f.locals[d]
+	if local.isPinned {
+		p.w("*(*%s)(unsafe.Pointer(%s%s/* %s */))", p.typ(n, paramTypeDecay(d)), f.bpName, nonZeroUintptr(local.off), local.name)
+		return
+	}
+
+	p.w("%s", local.name)
+}
+
+func (p *project) declaratorValueUnion(n cc.Node, f *function, d *cc.Declarator, t cc.Type, mode exprMode, flags flags) {
+	if d.Type().IsScalarType() {
+		defer p.w("%s", p.convertType(n, d.Type(), t, flags))
+	}
+	if f != nil {
+		if local := f.locals[d]; local != nil {
+			if local.isPinned {
+				p.w("*(*%s)(unsafe.Pointer(%s%s/* %s */))", p.typ(d, d.Type()), f.bpName, nonZeroUintptr(local.off), local.name)
+				return
+			}
+
+			p.w("%s", local.name)
+			return
+		}
+	}
+
+	p.declaratorDefault(n, d)
+}
+
+func (p *project) isVolatileOrAtomic(d *cc.Declarator) bool {
+	if d.Type().IsVolatile() || d.Type().IsAtomic() {
+		return true
+	}
+
+	_, ok := p.task.volatiles[d.Name()]
+	return ok
+}
+
+func (p *project) declaratorDefault(n cc.Node, d *cc.Declarator) {
+	if x := p.tlds[d]; x != nil && d.IsStatic() {
+		if p.isVolatileOrAtomic(d) {
+			p.atomicLoadNamedAddr(n, d.Type(), x.name)
+			return
+		}
+
+		p.w("%s", x.name)
+		return
+	}
+
+	switch x := p.symtab[d.Name().String()].(type) {
+	case *tld:
+		if p.isVolatileOrAtomic(d) {
+			p.atomicLoadNamedAddr(n, d.Type(), x.name)
+			return
+		}
+
+		p.w("%s", x.name)
+	case *imported:
+		x.used = true
+		if p.isVolatileOrAtomic(d) {
+			p.atomicLoadNamedAddr(n, d.Type(), fmt.Sprintf("%sX%s", x.qualifier, d.Name()))
+			return
+		}
+
+		p.w("%sX%s", x.qualifier, d.Name())
+	default:
+		if d.IsExtern() {
+			switch d.Name() {
+			case idEnviron:
+				if d.Type().String() == "pointer to pointer to char" {
+					p.w("%sEnviron()", p.task.crt)
+					return
+				}
+			}
+		}
+
+		if d.Linkage == cc.External && p.task.nostdlib {
+			p.w("X%s", d.Name())
+			return
+		}
+
+		id := fmt.Sprintf("__builtin_%s", d.Name())
+		switch x := p.symtab[id].(type) {
+		case *imported:
+			x.used = true
+			p.w("%sX%s", x.qualifier, d.Name())
+			return
+		}
+
+		if !d.IsImplicit() {
+			nm := d.Name()
+			name := nm.String()
+			switch d.Linkage {
+			case cc.External:
+				name = p.task.exportExterns + name
+				tld := &tld{name: name}
+				p.externs[nm] = tld
+				p.w("%s", name)
+				return
+			case cc.Internal:
+				if token.IsExported(name) {
+					name = "s" + name
+				}
+				tld := &tld{name: p.scope.take(cc.String(name))}
+				for _, v := range p.ast.Scope[nm] {
+					if d, ok := v.(*cc.Declarator); ok {
+						p.tlds[d] = tld
+					}
+				}
+				p.w("%s", name)
+				return
+			}
+		}
+
+		p.err(n, "back-end: undefined: %s", d.Name())
+	}
+}
+
+func (p *project) declaratorValueArray(n cc.Node, f *function, d *cc.Declarator, t cc.Type, mode exprMode, flags flags) {
+	if t.IsIntegerType() {
+		defer p.w("%s", p.convertType(n, nil, t, flags))
+	}
+	if f != nil {
+		if local := f.locals[d]; local != nil {
+			if local.isPinned {
+				p.w("(%s%s)/* %s */", f.bpName, nonZeroUintptr(local.off), local.name)
+				return
+			}
+
+			p.w("%s", local.name)
+			return
+		}
+	}
+
+	p.declaratorDefault(n, d)
+}
+
+func (p *project) declaratorValueNormal(n cc.Node, f *function, d *cc.Declarator, t cc.Type, mode exprMode, flags flags) {
+	if d.Type().IsScalarType() {
+		defer p.w("%s", p.convertType(n, d.Type(), t, flags))
+	}
+	if f != nil {
+		if local := f.locals[d]; local != nil {
+			if local.isPinned {
+				if p.isVolatileOrAtomic(d) && d.IsParameter && d.Write != 0 {
+					p.w("%sAtomicLoadP%s(%s%s/* %s */)", p.task.crt, p.helperType(n, d.Type()), f.bpName, nonZeroUintptr(local.off), local.name)
+					return
+				}
+
+				p.w("*(*%s)(unsafe.Pointer(%s%s/* %s */))", p.typ(d, d.Type()), f.bpName, nonZeroUintptr(local.off), local.name)
+				return
+			}
+
+			if p.isVolatileOrAtomic(d) && d.IsParameter && d.Write != 0 {
+				p.atomicLoadNamedAddr(n, d.Type(), local.name)
+				return
+			}
+
+			p.w("%s", local.name)
+			return
+		}
+	}
+
+	p.declaratorDefault(n, d)
+}
+
+func (p *project) declaratorFunc(n cc.Node, f *function, d *cc.Declarator, t cc.Type, mode exprMode, flags flags) {
+	switch k := p.declaratorKind(d); k {
+	case opFunction:
+		p.declaratorFuncFunc(n, f, d, t, exprValue, flags)
+	case opNormal:
+		p.declaratorFuncNormal(n, f, d, t, exprValue, flags)
+	default:
+		panic(todo("", d.Position(), k))
+	}
+}
+
+func (p *project) declaratorFuncNormal(n cc.Node, f *function, d *cc.Declarator, t cc.Type, mode exprMode, flags flags) {
+	u := d.Type()
+	if u.Kind() == cc.Ptr {
+		u = u.Elem()
+	}
+	switch u.Kind() {
+	case cc.Function:
+		if local := f.locals[d]; local != nil {
+			if local.isPinned {
+				p.w("(*(*")
+				p.functionSignature(n, f, u, "")
+				p.w(")(unsafe.Pointer(%s%s)))", f.bpName, nonZeroUintptr(local.off))
+				return
+			}
+
+			if d.IsParameter {
+				p.w("(*(*")
+				p.functionSignature(n, f, u, "")
+				p.w(")(unsafe.Pointer(&%s)))", local.name)
+				return
+			}
+
+			panic(todo("", p.pos(d)))
+		}
+
+		if x := p.tlds[d]; x != nil && d.IsStatic() {
+			p.w("(*(*")
+			p.functionSignature(n, f, u, "")
+			p.w(")(unsafe.Pointer(&%s)))", x.name)
+			return
+		}
+
+		switch x := p.symtab[d.Name().String()].(type) {
+		case *tld:
+			p.w("(*(*")
+			p.functionSignature(n, f, u, "")
+			p.w(")(unsafe.Pointer(&%s)))", x.name)
+		case *imported:
+			x.used = true
+			p.w("uintptr(unsafe.Pointer(&%sX%s))", x.qualifier, d.Name())
+		default:
+			panic(todo("%v: %v: %q", n.Position(), p.pos(d), d.Name()))
+		}
+	default:
+		panic(todo("", p.pos(d), u))
+	}
+}
+
+func (p *project) declaratorFuncFunc(n cc.Node, f *function, d *cc.Declarator, t cc.Type, mode exprMode, flags flags) {
+	switch d.Type().Kind() {
+	case cc.Function:
+		// ok
+	default:
+		panic(todo("", p.pos(d), d.Type(), d.Type().Kind()))
+	}
+
+	if f != nil {
+		if local := f.locals[d]; local != nil {
+			if local.isPinned {
+				panic(todo(""))
+			}
+
+			p.w(" %s", local.name)
+			return
+		}
+	}
+
+	p.declaratorDefault(n, d)
+}
+
+func (p *project) declaratorLValue(n cc.Node, f *function, d *cc.Declarator, t cc.Type, mode exprMode, flags flags) {
+	switch k := p.declaratorKind(d); k {
+	case opNormal, opArrayParameter, opUnion:
+		p.declaratorLValueNormal(n, f, d, t, mode, flags)
+	case opArray:
+		p.declaratorLValueArray(n, f, d, t, mode, flags)
+	default:
+		panic(todo("", d.Position(), k))
+	}
+}
+
+func (p *project) declaratorLValueArray(n cc.Node, f *function, d *cc.Declarator, t cc.Type, mode exprMode, flags flags) {
+	if f != nil {
+		if local := f.locals[d]; local != nil {
+			if local.isPinned {
+				p.w("*(*%s)(unsafe.Pointer(%s%s/* %s */))", p.typ(d, d.Type()), f.bpName, nonZeroUintptr(local.off), local.name)
+				return
+			}
+
+			p.w("%s", local.name)
+			return
+		}
+	}
+
+	p.declaratorDefault(n, d)
+}
+
+func (p *project) declaratorLValueNormal(n cc.Node, f *function, d *cc.Declarator, t cc.Type, mode exprMode, flags flags) {
+	if p.isVolatileOrAtomic(d) {
+		panic(todo("", n.Position(), d.Position()))
+	}
+
+	if d.Type().IsScalarType() {
+		defer p.w("%s", p.convertType(n, d.Type(), t, flags))
+	}
+	if f != nil {
+		if local := f.locals[d]; local != nil {
+			if local.isPinned {
+				p.w("*(*%s)(unsafe.Pointer(%s%s/* %s */))", p.dtyp(d), f.bpName, nonZeroUintptr(local.off), local.name)
+				return
+			}
+
+			p.w("%s", local.name)
+			return
+		}
+	}
+
+	p.declaratorLValueDefault(n, d)
+}
+
+func (p *project) declaratorLValueDefault(n cc.Node, d *cc.Declarator) {
+	if x := p.tlds[d]; x != nil && d.IsStatic() {
+		p.w("%s", x.name)
+		return
+	}
+
+	switch x := p.symtab[d.Name().String()].(type) {
+	case *tld:
+		p.w("%s", x.name)
+	case *imported:
+		x.used = true
+		p.w("%sX%s", x.qualifier, d.Name())
+	default:
+		if d.IsExtern() {
+			switch d.Name() {
+			case idEnviron:
+				if d.Type().String() == "pointer to pointer to char" {
+					p.w("*(*uintptr)(unsafe.Pointer(%sEnvironP()))", p.task.crt)
+					return
+				}
+			}
+		}
+
+		panic(todo("%v: %v: %q", n.Position(), p.pos(d), d.Name()))
+	}
+}
+
+func (p *project) declaratorKind(d *cc.Declarator) opKind {
+	switch {
+	case p.isArrayParameterDeclarator(d):
+		return opArrayParameter
+	case !p.pass1 && p.isArrayDeclarator(d):
+		return opArray
+	case d.Type().Kind() == cc.Function && !d.IsParameter:
+		return opFunction
+	case d.Type().Kind() == cc.Union:
+		return opUnion
+	default:
+		return opNormal
+	}
+}
+
+func (p *project) declaratorSelect(n cc.Node, f *function, d *cc.Declarator) {
+	switch k := p.declaratorKind(d); k {
+	case opNormal:
+		p.declaratorSelectNormal(n, f, d)
+	case opArray:
+		p.declaratorSelectArray(n, f, d)
+	default:
+		panic(todo("", d.Position(), k))
+	}
+}
+
+func (p *project) declaratorSelectArray(n cc.Node, f *function, d *cc.Declarator) {
+	if local := f.locals[d]; local != nil {
+		if local.isPinned {
+			panic(todo("", p.pos(n)))
+			//TODO type error
+			p.w("(*%s)(unsafe.Pointer(%s%s/* &%s */))", p.typ(d, d.Type()), f.bpName, nonZeroUintptr(local.off), local.name)
+			return
+		}
+
+		p.w("%s", local.name)
+		return
+	}
+
+	p.declaratorDefault(n, d)
+}
+
+func (p *project) declaratorSelectNormal(n cc.Node, f *function, d *cc.Declarator) {
+	if local := f.locals[d]; local != nil {
+		if local.isPinned {
+			p.w("(*%s)(unsafe.Pointer(%s%s/* &%s */))", p.typ(d, d.Type()), f.bpName, nonZeroUintptr(local.off), local.name)
+			return
+		}
+
+		p.w("%s", local.name)
+		return
+	}
+
+	p.declaratorDefault(n, d)
+}
+
+func (p *project) declaratorAddrOf(n cc.Node, f *function, d *cc.Declarator, t cc.Type, flags flags) {
+	switch k := p.declaratorKind(d); k {
+	case opArray:
+		p.declaratorAddrOfArray(n, f, d)
+	case opNormal:
+		p.declaratorAddrOfNormal(n, f, d, flags)
+	case opUnion:
+		p.declaratorAddrOfUnion(n, f, d)
+	case opFunction:
+		p.declaratorAddrOfFunction(n, f, d)
+	case opArrayParameter:
+		p.declaratorAddrOfArrayParameter(n, f, d)
+	default:
+		panic(todo("", d.Position(), k))
+	}
+}
+
+func (p *project) declaratorAddrOfArrayParameter(n cc.Node, f *function, d *cc.Declarator) {
+	if p.pass1 {
+		f.pin(n, d)
+		return
+	}
+
+	local := f.locals[d]
+	p.w("(%s%s)/* &%s */", f.bpName, nonZeroUintptr(local.off), local.name)
+}
+
+func (p *project) declaratorAddrOfFunction(n cc.Node, f *function, d *cc.Declarator) {
+	if d.Type().Kind() != cc.Function {
+		panic(todo("", p.pos(n)))
+	}
+
+	if x := p.tlds[d]; x != nil && d.IsStatic() {
+		p.w("*(*uintptr)(unsafe.Pointer(&struct{f ")
+		p.functionSignature(n, f, d.Type(), "")
+		p.w("}{%s}))", x.name)
+		return
+	}
+
+	switch x := p.symtab[d.Name().String()].(type) {
+	case *tld:
+		p.w("*(*uintptr)(unsafe.Pointer(&struct{f ")
+		p.functionSignature(n, f, d.Type(), "")
+		p.w("}{%s}))", x.name)
+	case *imported:
+		x.used = true
+		p.w("*(*uintptr)(unsafe.Pointer(&struct{f ")
+		p.functionSignature(n, f, d.Type(), "")
+		p.w("}{%sX%s}))", x.qualifier, d.Name())
+	default:
+		p.err(d, "back-end: undefined: %s", d.Name())
+	}
+}
+
+func (p *project) declaratorAddrOfUnion(n cc.Node, f *function, d *cc.Declarator) {
+	if f != nil {
+		if local := f.locals[d]; local != nil {
+			if p.pass1 {
+				f.pin(n, d)
+				return
+			}
+
+			if local.isPinned {
+				p.w("(%s%s)/* &%s */", f.bpName, nonZeroUintptr(local.off), local.name)
+				return
+			}
+
+			panic(todo("", p.pos(n)))
+		}
+	}
+
+	if x := p.tlds[d]; x != nil && d.IsStatic() {
+		p.w("uintptr(unsafe.Pointer(&%s))", x.name)
+		return
+	}
+
+	switch x := p.symtab[d.Name().String()].(type) {
+	case *tld:
+		p.w("uintptr(unsafe.Pointer(&%s))", x.name)
+	case *imported:
+		x.used = true
+		p.w("uintptr(unsafe.Pointer(&%sX%s))", x.qualifier, d.Name())
+	default:
+		panic(todo("%v: %v: %q", n.Position(), p.pos(d), d.Name()))
+	}
+}
+
+func (p *project) declaratorAddrOfNormal(n cc.Node, f *function, d *cc.Declarator, flags flags) {
+	if f != nil {
+		if local := f.locals[d]; local != nil {
+			if p.pass1 && flags&fAddrOfFuncPtrOk == 0 {
+				f.pin(n, d)
+				return
+			}
+
+			if local.isPinned {
+				p.w("(%s%s)/* &%s */", f.bpName, nonZeroUintptr(local.off), local.name)
+				return
+			}
+
+			if flags&fAddrOfFuncPtrOk != 0 {
+				if dt := d.Type(); dt.Kind() == cc.Ptr {
+					if elem := dt.Elem(); elem.Kind() == cc.Function || elem.Kind() == cc.Ptr && elem.Elem().Kind() == cc.Function {
+						p.w("&%s", local.name)
+						return
+					}
+				}
+			}
+
+			panic(todo("", p.pos(n), p.pos(d), d.Name(), d.Type(), d.IsParameter, d.AddressTaken, flags&fAddrOfFuncPtrOk != 0))
+		}
+	}
+
+	if x := p.tlds[d]; x != nil && d.IsStatic() {
+		p.w("uintptr(unsafe.Pointer(&%s))", x.name)
+		return
+	}
+
+	switch x := p.symtab[d.Name().String()].(type) {
+	case *tld:
+		p.w("uintptr(unsafe.Pointer(&%s))", x.name)
+	case *imported:
+		x.used = true
+		p.w("uintptr(unsafe.Pointer(&%sX%s))", x.qualifier, d.Name())
+	default:
+		p.err(n, "undefined: %s", d.Name())
+	}
+}
+
+func (p *project) declaratorAddrOfArray(n cc.Node, f *function, d *cc.Declarator) {
+	if f != nil {
+		if local := f.locals[d]; local != nil {
+			if p.pass1 {
+				f.pin(n, d)
+				return
+			}
+
+			if local.isPinned {
+				p.w("(%s%s)/* &%s */", f.bpName, nonZeroUintptr(local.off), local.name)
+				return
+			}
+
+			panic(todo("", p.pos(d), d.Name(), d.Type(), d.IsParameter))
+		}
+	}
+
+	if x := p.tlds[d]; x != nil && d.IsStatic() {
+		p.w("uintptr(unsafe.Pointer(&%s))", x.name)
+		return
+	}
+
+	switch x := p.symtab[d.Name().String()].(type) {
+	case *tld:
+		p.w("uintptr(unsafe.Pointer(&%s))", x.name)
+	case *imported:
+		x.used = true
+		p.w("uintptr(unsafe.Pointer(&%sX%s))", x.qualifier, d.Name())
+	default:
+		panic(todo("%v: %v: %q", n.Position(), p.pos(d), d.Name()))
+	}
+}
+
+func (p *project) convertType(n cc.Node, from, to cc.Type, flags flags) string {
+	// trc("%v: %v: %v -> %v %v", n.Position(), origin(1), from, to, flags) //TODO- DBG
+	if from != nil {
+		switch from.Kind() {
+		case cc.Int128:
+			return p.convertTypeFromInt128(n, to, flags)
+		case cc.UInt128:
+			return p.convertTypeFromUint128(n, to, flags)
+		}
+	}
+
+	switch to.Kind() {
+	case cc.Int128:
+		return p.convertTypeToInt128(n, from, flags)
+	case cc.UInt128:
+		return p.convertTypeToUint128(n, from, flags)
+	}
+
+	// trc("%v: %v -> %v\n%s", p.pos(n), from, to, debug.Stack()[:600]) //TODO-
+	force := flags&fForceConv != 0
+	if from == nil {
+		p.w("%s(", p.typ(n, to))
+		return ")"
+	}
+
+	if from.IsScalarType() {
+		switch {
+		case force:
+			p.w("%s(", p.helperType2(n, from, to))
+			return ")"
+		case from.Kind() == to.Kind():
+			return ""
+		default:
+			p.w("%s(", p.typ(n, to))
+			return ")"
+		}
+	}
+
+	switch from.Kind() {
+	case cc.Function, cc.Struct, cc.Union, cc.Ptr, cc.Array:
+		if from.Kind() == to.Kind() {
+			return ""
+		}
+
+		panic(todo("", n.Position(), from, to, from.Alias(), to.Alias()))
+	case cc.Double, cc.Float:
+		p.w("%s(", p.typ(n, to))
+		return ")"
+	}
+
+	panic(todo("", n.Position(), from, to, from.Alias(), to.Alias()))
+}
+
+func (p *project) convertTypeFromInt128(n cc.Node, to cc.Type, flags flags) string {
+	switch k := to.Kind(); {
+	case k == cc.Float, k == cc.Double:
+		p.w("(")
+		return fmt.Sprintf(").Float%d()", to.Size()*8)
+	case k == cc.Int128:
+		return ""
+	case k == cc.UInt128:
+		p.w("%sUint128FromInt128(", p.task.crt)
+		return ")"
+	case to.IsIntegerType() && to.IsSignedType():
+		p.w("int%d((", to.Size()*8)
+		return ").Lo)"
+	case to.IsIntegerType() && !to.IsSignedType():
+		p.w("uint%d((", to.Size()*8)
+		return ").Lo)"
+	default:
+		panic(todo("", n.Position(), to, to.Alias()))
+	}
+}
+
+func (p *project) convertTypeFromUint128(n cc.Node, to cc.Type, flags flags) string {
+	switch k := to.Kind(); {
+	case k == cc.Float, k == cc.Double:
+		p.w("(")
+		return fmt.Sprintf(").Float%d()", to.Size()*8)
+	case k == cc.Int128:
+		p.w("(")
+		return ").Int128()"
+	case k == cc.UInt128:
+		return ""
+	case to.IsIntegerType() && to.IsSignedType():
+		p.w("int%d((", to.Size()*8)
+		return ").Lo)"
+	case to.IsIntegerType() && !to.IsSignedType():
+		p.w("uint%d((", to.Size()*8)
+		return ").Lo)"
+	default:
+		panic(todo("", n.Position(), to, to.Alias()))
+	}
+}
+
+func (p *project) convertTypeToInt128(n cc.Node, from cc.Type, flags flags) string {
+	switch k := from.Kind(); {
+	case k == cc.Float, k == cc.Double:
+		p.w("%sInt128FromFloat%d(", p.task.crt, from.Size()*8)
+		return ")"
+	case k == cc.Int128:
+		return ""
+	case k == cc.UInt128:
+		p.w("%sInt128FromUint128(", p.task.crt)
+		return ")"
+	case from.IsIntegerType() && from.IsSignedType():
+		p.w("%sInt128FromInt%d(", p.task.crt, from.Size()*8)
+		return ")"
+	case from.IsIntegerType() && !from.IsSignedType():
+		p.w("%sInt128FromUint%d(", p.task.crt, from.Size()*8)
+		return ")"
+	default:
+		panic(todo("", n.Position(), from, from.Alias()))
+	}
+}
+
+func (p *project) convertTypeToUint128(n cc.Node, from cc.Type, flags flags) string {
+	switch k := from.Kind(); {
+	case k == cc.Float, k == cc.Double:
+		p.w("%sUint128FromFloat%d(", p.task.crt, from.Size()*8)
+		return ")"
+	case k == cc.Int128:
+		p.w("(")
+		return ").Uint128()"
+	case k == cc.UInt128:
+		return ""
+	case from.IsIntegerType() && from.IsSignedType():
+		p.w("%sUint128FromInt%d(", p.task.crt, from.Size()*8)
+		return ")"
+	case from.IsIntegerType() && !from.IsSignedType():
+		p.w("%sUint128FromUint%d(", p.task.crt, from.Size()*8)
+		return ")"
+	default:
+		panic(todo("", n.Position(), from, from.Alias()))
+	}
+}
+
+func (p *project) convertFromInt128(n cc.Node, op cc.Operand, to cc.Type, flags flags) string {
+	switch k := to.Kind(); {
+	case k == cc.Float, k == cc.Double:
+		p.w("(")
+		return fmt.Sprintf(").Float%d()", to.Size()*8)
+	case k == cc.Int128:
+		return ""
+	case k == cc.UInt128:
+		p.w("(")
+		return ").Uint128()"
+	case to.IsIntegerType() && to.IsSignedType():
+		p.w("%sInt%d(", p.task.crt, to.Size()*8)
+		return ")"
+	case to.IsIntegerType() && !to.IsSignedType():
+		p.w("%sUint%d(", p.task.crt, to.Size()*8)
+		return ")"
+	default:
+		panic(todo("", n.Position(), to, to.Alias()))
+	}
+}
+
+func (p *project) convertFromUint128(n cc.Node, op cc.Operand, to cc.Type, flags flags) string {
+	switch k := to.Kind(); {
+	case k == cc.Float, k == cc.Double:
+		p.w("%sUint128FromFloat%d(", p.task.crt, to.Size()*8)
+		return ")"
+	case k == cc.Int128:
+		p.w("(")
+		return ").Int128()"
+	case k == cc.UInt128:
+		return ""
+	case to.IsIntegerType() && to.IsSignedType():
+		p.w("%sInt%d(", p.task.crt, to.Size()*8)
+		return ")"
+	case to.IsIntegerType() && !to.IsSignedType():
+		p.w("%sUint%d(", p.task.crt, to.Size()*8)
+		return ")"
+	default:
+		panic(todo("", n.Position(), to, to.Alias()))
+	}
+}
+
+func (p *project) convertToInt128(n cc.Node, op cc.Operand, to cc.Type, flags flags) string {
+	from := op.Type()
+	switch k := from.Kind(); {
+	case k == cc.Float, k == cc.Double:
+		p.w("%sInt128FromFloat%d(", p.task.crt, from.Size()*8)
+		return ")"
+	case k == cc.Int128:
+		return ""
+	case k == cc.UInt128:
+		p.w("(")
+		return ").Int128()"
+	case from.IsIntegerType() && from.IsSignedType():
+		p.w("%sInt128FromInt%d(", p.task.crt, from.Size()*8)
+		return ")"
+	case from.IsIntegerType() && !from.IsSignedType():
+		p.w("%sInt128FromUint%d(", p.task.crt, from.Size()*8)
+		return ")"
+	default:
+		panic(todo("", n.Position(), from, from.Alias()))
+	}
+}
+
+func (p *project) convertToUint128(n cc.Node, op cc.Operand, to cc.Type, flags flags) string {
+	from := op.Type()
+	switch k := from.Kind(); {
+	case k == cc.Float, k == cc.Double:
+		p.w("%sUint128FromFloat%d(", p.task.crt, from.Size()*8)
+		return ")"
+	case k == cc.Int128:
+		p.w("(")
+		return ").Uint128()"
+	case k == cc.UInt128:
+		return ""
+	case from.IsIntegerType() && from.IsSignedType():
+		p.w("%sUint128FromInt%d(", p.task.crt, from.Size()*8)
+		return ")"
+	case from.IsIntegerType() && !from.IsSignedType():
+		p.w("%sUint128FromUint%d(", p.task.crt, from.Size()*8)
+		return ")"
+	default:
+		panic(todo("", n.Position(), from, from.Alias()))
+	}
+}
+
+func (p *project) convertNil(n cc.Node, to cc.Type, flags flags) string {
+	switch to.Kind() {
+	case cc.Int128:
+		panic(todo("", pos(n)))
+	case cc.UInt128:
+		panic(todo("", pos(n)))
+	}
+
+	p.w("%s(", p.typ(n, to))
+	return ")"
+}
+
+func (p *project) convert(n cc.Node, op cc.Operand, to cc.Type, flags flags) string {
+	if op == nil {
+		panic(todo("internal error"))
+	}
+
+	from := op.Type()
+	switch from.Kind() {
+	case cc.Int128:
+		return p.convertFromInt128(n, op, to, flags)
+	case cc.UInt128:
+		return p.convertFromUint128(n, op, to, flags)
+	}
+	switch to.Kind() {
+	case cc.Int128:
+		return p.convertToInt128(n, op, to, flags)
+	case cc.UInt128:
+		return p.convertToUint128(n, op, to, flags)
+	}
+
+	if flags&fForceRuntimeConv != 0 {
+		flags |= fForceConv
+	}
+	force := flags&fForceConv != 0
+	if !force && from.IsScalarType() && from.Kind() == to.Kind() {
+		return ""
+	}
+
+	if from.IsIntegerType() {
+		return p.convertInt(n, op, to, flags)
+	}
+
+	if from == to {
+		return ""
+	}
+
+	switch from.Kind() {
+	case cc.Ptr:
+		if !force && from.Kind() == to.Kind() {
+			return ""
+		}
+
+		if to.IsIntegerType() {
+			p.w("%s(", p.typ(n, to))
+			return ")"
+		}
+
+		if to.Kind() == cc.Ptr {
+			return ""
+		}
+
+		panic(todo("%v: force %v, %q %v -> %q %v", p.pos(n), force, from, from.Kind(), to, to.Kind()))
+	case cc.Function, cc.Struct, cc.Union:
+		if !force && from.Kind() == to.Kind() {
+			return ""
+		}
+
+		trc("%p %p", from, to)
+		panic(todo("%q %v -> %q %v", from, from.Kind(), to, to.Kind()))
+	case cc.Double, cc.Float:
+		switch {
+		case to.IsIntegerType():
+			p.w("%s(", p.helperType2(n, from, to))
+			return ")"
+		default:
+			p.w("%s(", p.typ(n, to))
+			return ")"
+		}
+	case cc.Array:
+		if from.Kind() == to.Kind() {
+			return ""
+		}
+
+		switch to.Kind() {
+		case cc.Ptr:
+			return ""
+		}
+
+		panic(todo("%q, %v -> %q, %v", from, from.Kind(), to.Kind()))
+	}
+
+	panic(todo("%q -> %q", from, to))
+}
+
+func (p *project) convertInt(n cc.Node, op cc.Operand, to cc.Type, flags flags) string {
+	from := op.Type()
+	switch from.Kind() {
+	case cc.Int128:
+		panic(todo("", pos(n)))
+	case cc.UInt128:
+		panic(todo("", pos(n)))
+	}
+	switch to.Kind() {
+	case cc.Int128:
+		panic(todo("", pos(n)))
+	case cc.UInt128:
+		panic(todo("", pos(n)))
+	}
+
+	force := flags&fForceConv != 0
+	value := op.Value()
+	if value == nil || !to.IsIntegerType() {
+		if to.IsScalarType() {
+			p.w("%s(", p.typ(n, to))
+			return ")"
+		}
+
+		panic(todo("", op.Type(), to))
+	}
+
+	if flags&fForceRuntimeConv != 0 {
+		p.w("%s(", p.helperType2(n, op.Type(), to))
+		return ")"
+	}
+
+	switch {
+	case from.IsSignedType():
+		switch {
+		case to.IsSignedType():
+			switch x := value.(type) {
+			case cc.Int64Value:
+				switch to.Size() {
+				case 1:
+					if x >= math.MinInt8 && x <= math.MaxInt8 {
+						switch {
+						case !force && from.Size() == to.Size():
+							return ""
+						default:
+							p.w("int8(")
+							return ")"
+						}
+					}
+
+					p.w("%sInt8FromInt%d(", p.task.crt, from.Size()*8)
+					return ")"
+				case 2:
+					if x >= math.MinInt16 && x <= math.MaxInt16 {
+						switch {
+						case !force && from.Size() == to.Size():
+							return ""
+						default:
+							p.w("int16(")
+							return ")"
+						}
+					}
+
+					p.w("%sInt16FromInt%d(", p.task.crt, from.Size()*8)
+					return ")"
+				case 4:
+					if x >= math.MinInt32 && x <= math.MaxInt32 {
+						switch {
+						case !force && from.Size() == to.Size():
+							return ""
+						default:
+							p.w("int32(")
+							return ")"
+						}
+					}
+
+					p.w("%sInt32FromInt%d(", p.task.crt, from.Size()*8)
+					return ")"
+				case 8:
+					switch {
+					case !force && from.Size() == to.Size():
+						return ""
+					default:
+						p.w("int64(")
+						return ")"
+					}
+				default:
+					panic(todo("%T(%v) %v -> %v", x, op.Value(), from, to))
+				}
+			default:
+				panic(todo("%T(%v) %v -> %v", x, op.Value(), from, to))
+			}
+		default: // to is unsigned
+			switch x := value.(type) {
+			case cc.Int64Value:
+				switch to.Size() {
+				case 1:
+					if x >= 0 && x <= math.MaxUint8 {
+						p.w("%s(", p.typ(n, to))
+						return ")"
+					}
+
+					p.w("%sUint8FromInt%d(", p.task.crt, from.Size()*8)
+					return ")"
+				case 2:
+					if x >= 0 && x <= math.MaxUint16 {
+						p.w("%s(", p.typ(n, to))
+						return ")"
+					}
+
+					p.w("%sUint16FromInt%d(", p.task.crt, from.Size()*8)
+					return ")"
+				case 4:
+					if x >= 0 && x <= math.MaxUint32 {
+						p.w("%s(", p.typ(n, to))
+						return ")"
+					}
+
+					p.w("%sUint32FromInt%d(", p.task.crt, from.Size()*8)
+					return ")"
+				case 8:
+					if x >= 0 {
+						p.w("uint64(")
+						return ")"
+					}
+
+					p.w("%sUint64FromInt%d(", p.task.crt, from.Size()*8)
+					return ")"
+				default:
+					panic(todo("%T(%v) %v -> %v", x, op.Value(), from, to))
+				}
+			case cc.Uint64Value:
+				switch to.Size() {
+				case 1:
+					if x <= math.MaxUint8 {
+						p.w("%s(", p.typ(n, to))
+						return ")"
+					}
+
+					p.w("%sUint8FromUint%d(", p.task.crt, from.Size()*8)
+					return ")"
+				case 2:
+					if x <= math.MaxUint16 {
+						p.w("%s(", p.typ(n, to))
+						return ")"
+					}
+
+					p.w("%sUint16FromUint%d(", p.task.crt, from.Size()*8)
+					return ")"
+				case 4:
+					if x <= math.MaxUint32 {
+						p.w("%s(", p.typ(n, to))
+						return ")"
+					}
+
+					p.w("%sUint32FromUint%d(", p.task.crt, from.Size()*8)
+					return ")"
+				case 8:
+					p.w("uint64(")
+					return ")"
+				default:
+					panic(todo("%T(%v) %v -> %v", x, op.Value(), from, to))
+				}
+			default:
+				panic(todo("%T(%v) %v -> %v", x, op.Value(), from, to))
+			}
+		}
+	default: // from is unsigned
+		switch {
+		case to.IsSignedType():
+			switch x := value.(type) {
+			case cc.Uint64Value:
+				switch to.Size() {
+				case 1:
+					if x <= math.MaxInt8 {
+						p.w("int8(")
+						return ")"
+					}
+
+					p.w("%sInt8FromUint%d(", p.task.crt, from.Size()*8)
+					return ")"
+				case 2:
+					if x <= math.MaxInt16 {
+						p.w("int16(")
+						return ")"
+					}
+
+					p.w("%sInt16FromUint%d(", p.task.crt, from.Size()*8)
+					return ")"
+				case 4:
+					if x <= math.MaxInt32 {
+						p.w("int32(")
+						return ")"
+					}
+
+					p.w("%sInt32FromUint%d(", p.task.crt, from.Size()*8)
+					return ")"
+				case 8:
+					if x <= math.MaxInt64 {
+						p.w("int64(")
+						return ")"
+					}
+
+					p.w("%sInt64FromUint%d(", p.task.crt, from.Size()*8)
+					return ")"
+				default:
+					panic(todo("%T(%v) %v -> %v", x, op.Value(), from, to))
+				}
+			default:
+				panic(todo("%T(%v) %v -> %v", x, op.Value(), from, to))
+			}
+		default: // to is unsigned
+			switch x := value.(type) {
+			case cc.Uint64Value:
+				switch to.Size() {
+				case 1:
+					if x <= math.MaxUint8 {
+						switch {
+						case !force && from.Size() == 1:
+							return ""
+						default:
+							p.w("uint8(")
+							return ")"
+						}
+					}
+
+					p.w("%sUint8FromUint%d(", p.task.crt, from.Size()*8)
+					return ")"
+				case 2:
+					if x <= math.MaxUint16 {
+						switch {
+						case !force && from.Size() == 2:
+							return ""
+						default:
+							p.w("uint16(")
+							return ")"
+						}
+					}
+
+					p.w("%sUint16FromUint%d(", p.task.crt, from.Size()*8)
+					return ")"
+				case 4:
+					if x <= math.MaxUint32 {
+						switch {
+						case !force && from.Size() == 4:
+							return ""
+						default:
+							p.w("uint32(")
+							return ")"
+						}
+					}
+
+					p.w("%sUint32FromUint%d(", p.task.crt, from.Size()*8)
+					return ")"
+				case 8:
+					switch {
+					case !force && from.Size() == 8:
+						return ""
+					default:
+						p.w("uint64(")
+						return ")"
+					}
+				default:
+					panic(todo("%T(%v) %v -> %v", x, op.Value(), from, to))
+				}
+			default:
+				panic(todo("%T(%v) %v -> %v", x, op.Value(), from, to))
+			}
+		}
+	}
+}
+
+func nonZeroUintptr(n uintptr) string {
+	if n == 0 {
+		return ""
+	}
+
+	return fmt.Sprintf("%+d", n)
+}
+
+func alias(attr []*cc.AttributeSpecifier) (r cc.StringID) {
+	for _, v := range attr {
+		cc.Inspect(v, func(n cc.Node, entry bool) bool {
+			if !entry {
+				return true
+			}
+
+			if x, ok := n.(*cc.AttributeValue); ok && x.Token.Value == idAlias {
+				switch y := x.ExpressionList.AssignmentExpression.Operand.Value().(type) {
+				case cc.StringValue:
+					r = cc.StringID(y)
+					return false
+				}
+			}
+			return true
+		})
+		if r != 0 {
+			return r
+		}
+	}
+	return 0
+}
+
+func (p *project) tld(f *function, n *cc.InitDeclarator, sep string, staticLocal bool) {
+	d := n.Declarator
+	if d.IsExtern() && d.Linkage == cc.External && !d.IsTypedefName {
+		if alias := alias(attrs(d.Type())); alias != 0 {
+			p.capi = append(p.capi, d.Name().String())
+			p.w("\n\nvar %s%s = %s\t// %v:\n", p.task.exportExterns, d.Name(), p.externs[alias].name, p.pos(d))
+			return
+		}
+	}
+
+	if _, ok := p.wanted[d]; !ok && !staticLocal {
+		isFn := d.Type().Kind() == cc.Function
+		if isFn && p.task.header && p.task.funcSig {
+			if nm := d.Name().String(); !strings.HasPrefix(nm, "__") {
+				p.w("\n\n")
+				t := p.tlds[d]
+				if t == nil {
+					t = &tld{}
+					t.name = p.tldScope.take(d.Name())
+				}
+				p.functionSignature2(n, nil, d.Type(), t.name)
+			}
+		}
+		return
+	}
+
+	tld := p.tlds[d]
+	if tld == nil { // Dead declaration.
+		return
+	}
+
+	t := d.Type()
+	if d.IsTypedefName {
+		p.checkAttributes(t)
+		if _, ok := p.typedefsEmited[tld.name]; ok {
+			return
+		}
+
+		p.typedefsEmited[tld.name] = struct{}{}
+		if t.Kind() != cc.Void {
+			p.w("%stype %s = %s; /* %v */", sep, tld.name, p.typ(n, t), p.pos(d))
+		}
+		return
+	}
+
+	switch n.Case {
+	case cc.InitDeclaratorDecl: // Declarator AttributeSpecifierList
+		p.w("%svar %s %s\t/* %v: */", sep, tld.name, p.typ(n, t), p.pos(n))
+		switch t.Kind() {
+		case cc.Struct, cc.Union:
+			p.structs[t.Tag()].emit(p, nil)
+		}
+	case cc.InitDeclaratorInit: // Declarator AttributeSpecifierList '=' Initializer
+		if d.IsStatic() && d.Read == 0 && d.Write == 1 && n.Initializer.IsConst() { // Initialized with no side effects and unused.
+			break
+		}
+
+		p.w("%svar %s ", sep, tld.name)
+		if !isAggregateTypeOrUnion(d.Type()) {
+			p.w("%s ", p.typ(n, d.Type()))
+		}
+		p.w("= ")
+		p.initializer(f, n.Initializer, d.Type(), d.StorageClass, tld)
+		p.w("; /* %v */", p.pos(d))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) functionDefinition(n *cc.FunctionDefinition) {
+	// DeclarationSpecifiers Declarator DeclarationList CompoundStatement
+	if p.task.header && !p.task.funcSig {
+		return
+	}
+
+	if _, ok := p.sharedFns[n]; ok {
+		if _, ok := p.sharedFnsEmitted[n]; ok {
+			return
+		}
+
+		p.sharedFnsEmitted[n] = struct{}{}
+	}
+
+	d := n.Declarator
+	if d.IsExtern() && d.Type().Inline() {
+		// https://gcc.gnu.org/onlinedocs/gcc/Inline.html
+		//
+		// If you specify both inline and extern in the function definition, then the
+		// definition is used only for inlining. In no case is the function compiled on
+		// its own, not even if you refer to its address explicitly. Such an address
+		// becomes an external reference, as if you had only declared the function, and
+		// had not defined it.
+		//
+		// This combination of inline and extern has almost the effect of a macro. The
+		// way to use it is to put a function definition in a header file with these
+		// keywords, and put another copy of the definition (lacking inline and extern)
+		// in a library file. The definition in the header file causes most calls to
+		// the function to be inlined. If any uses of the function remain, they refer
+		// to the single copy in the library.
+		return
+	}
+
+	name := d.Name().String()
+	if _, ok := p.task.hide[name]; ok {
+		return
+	}
+
+	if p.isMain && d.Linkage == cc.External && d.Read == 0 && !d.AddressTaken && len(p.task.asts) == 1 {
+		return
+	}
+
+	if d.Linkage == cc.Internal && d.Read == 0 && !d.AddressTaken /*TODO- && strings.HasPrefix(name, "__") */ {
+		return
+	}
+
+	tld := p.tlds[d]
+	if tld == nil {
+		return
+	}
+
+	p.fn = name
+
+	defer func() { p.fn = "" }()
+
+	f := newFunction(p, n)
+	p.pass1 = true
+	p.compoundStatement(f, n.CompoundStatement, "", false, false, 0)
+	p.pass1 = false
+	p.w("\n\n")
+	p.functionDefinitionSignature(n, f, tld)
+	if p.task.header && p.task.funcSig {
+		return
+	}
+
+	p.w(" ")
+	comment := fmt.Sprintf("/* %v: */", p.pos(d))
+	if p.task.panicStubs {
+		p.w("%s{ panic(%q) }", comment, tld.name)
+		return
+	}
+
+	brace := "{"
+	if need := f.off; need != 0 {
+		scope := f.blocks[n.CompoundStatement].scope
+		f.bpName = scope.take(idBp)
+		p.w("{%s\n%s := %s.Alloc(%d)\n", comment, f.bpName, f.tlsName, need)
+		p.w("defer %s.Free(%d)\n", f.tlsName, need)
+		for _, v := range d.Type().Parameters() {
+			if local := f.locals[v.Declarator()]; local != nil && local.isPinned { // Pin it.
+				p.w("*(*%s)(unsafe.Pointer(%s%s)) = %s\n", p.typ(v.Declarator(), paramTypeDecay(v.Declarator())), f.bpName, nonZeroUintptr(local.off), local.name)
+			}
+		}
+		comment = ""
+		brace = ""
+	}
+	if len(f.vlas) != 0 {
+		p.w("%s%s\n", brace, comment)
+		var vlas []*cc.Declarator
+		for k := range f.vlas {
+			vlas = append(vlas, k)
+		}
+		sort.Slice(vlas, func(i, j int) bool {
+			return vlas[i].NameTok().Seq() < vlas[j].NameTok().Seq()
+		})
+		for _, v := range vlas {
+			local := f.locals[v]
+			switch {
+			case local.isPinned:
+				panic(todo("", v.Position()))
+			default:
+				p.w("var %s uintptr // %v: %v\n", local.name, p.pos(v), v.Type())
+			}
+		}
+		switch {
+		case len(vlas) == 1:
+			p.w("defer %sXfree(%s, %s)\n", p.task.crt, f.tlsName, f.locals[vlas[0]].name)
+		default:
+			p.w("defer func() {\n")
+			for _, v := range vlas {
+				p.w("%sXfree(%s, %s)\n", p.task.crt, f.tlsName, f.locals[v].name)
+			}
+			p.w("\n}()\n")
+		}
+	}
+	p.compoundStatement(f, n.CompoundStatement, comment, false, true, 0)
+	p.w(";")
+	p.flushLocalTaggesStructs()
+	p.flushStaticTLDs()
+}
+
+func (p *project) flushLocalTaggesStructs() {
+	for _, v := range p.localTaggedStructs {
+		v()
+	}
+	p.localTaggedStructs = nil
+}
+
+func (p *project) flushStaticTLDs() {
+	for _, v := range p.staticQueue {
+		p.tld(nil, v, "\n", true)
+	}
+	p.staticQueue = nil
+}
+
+func (p *project) compoundStatement(f *function, n *cc.CompoundStatement, scomment string, forceNoBraces, fnBody bool, mode exprMode) {
+	if p.task.panicStubs {
+		return
+	}
+
+	// '{' BlockItemList '}'
+	brace := (!n.IsJumpTarget() || n.Parent() == nil) && !forceNoBraces
+	if brace && len(f.vlas) == 0 && (n.Parent() != nil || f.off == 0) {
+		p.w("{%s", scomment)
+	}
+	if fnBody {
+		p.instrument(n)
+	}
+	sv := f.block
+	f.block = f.blocks[n]
+	if f.block.topDecl {
+		for _, v := range f.block.decls {
+			p.declaration(f, v, true)
+		}
+	}
+	var r *cc.JumpStatement
+	for list := n.BlockItemList; list != nil; list = list.BlockItemList {
+		m := mode
+		if list.BlockItemList != nil {
+			m = 0
+		}
+		r = p.blockItem(f, list.BlockItem, m)
+	}
+	if n.Parent() == nil && r == nil && f.rt.Kind() != cc.Void {
+		p.w("\nreturn ")
+		p.zeroValue(n, f.rt)
+	}
+	s := tidyComment("\n", &n.Token2)
+	p.w("%s", s)
+	if brace {
+		if !strings.HasSuffix(s, "\n") {
+			p.w("\n")
+		}
+		p.w("}")
+	}
+	f.block = sv
+}
+
+func (p *project) zeroValue(n cc.Node, t cc.Type) {
+	if t.IsScalarType() {
+		p.w("%s(0)", p.typ(n, t))
+		return
+	}
+
+	switch t.Kind() {
+	case cc.Struct, cc.Union:
+		p.w("%s{}", p.typ(n, t))
+	default:
+		panic(todo("", t, t.Kind()))
+	}
+}
+
+func (p *project) blockItem(f *function, n *cc.BlockItem, mode exprMode) (r *cc.JumpStatement) {
+	switch n.Case {
+	case cc.BlockItemDecl: // Declaration
+		p.declaration(f, n.Declaration, false)
+	case cc.BlockItemStmt: // Statement
+		r = p.statement(f, n.Statement, false, false, false, mode)
+		p.w(";")
+		if r == nil {
+			p.instrument(n)
+		}
+	case cc.BlockItemLabel: // LabelDeclaration
+		panic(todo("", p.pos(n)))
+		p.w(";")
+	case cc.BlockItemFuncDef: // DeclarationSpecifiers Declarator CompoundStatement
+		p.err(n, "nested functions not supported")
+		p.w(";")
+	case cc.BlockItemPragma: // PragmaSTDC
+		panic(todo("", p.pos(n)))
+		p.w(";")
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+	return r
+}
+
+func (p *project) instrument(n cc.Node) {
+	if p.task.cover {
+		p.w("%sCover();", p.task.crt)
+	}
+	if p.task.coverC {
+		p.w("%sCoverC(%q);", p.task.crt, p.pos(n).String()+" "+p.fn)
+	}
+	if p.task.watch {
+		p.w("%sWatch();", p.task.crt)
+	}
+}
+
+var dummyJumpStatement = &cc.JumpStatement{}
+
+func (p *project) statement(f *function, n *cc.Statement, forceCompoundStmtBrace, forceNoBraces, switchBlock bool, mode exprMode) (r *cc.JumpStatement) {
+	if forceCompoundStmtBrace {
+		p.w(" {")
+		if !switchBlock {
+			p.instrument(n)
+		}
+	}
+	switch n.Case {
+	case cc.StatementLabeled: // LabeledStatement
+		r = p.labeledStatement(f, n.LabeledStatement)
+	case cc.StatementCompound: // CompoundStatement
+		if !forceCompoundStmtBrace {
+			p.w("%s", n.CompoundStatement.Token.Sep)
+		}
+		if f.hasJumps {
+			forceNoBraces = true
+		}
+		p.compoundStatement(f, n.CompoundStatement, "", forceCompoundStmtBrace || forceNoBraces, false, 0)
+	case cc.StatementExpr: // ExpressionStatement
+		if mode != 0 {
+			p.w("return ")
+			e := n.ExpressionStatement.Expression
+			p.expression(f, e, e.Operand.Type(), exprValue, 0)
+			r = dummyJumpStatement
+			break
+		}
+
+		p.expressionStatement(f, n.ExpressionStatement)
+	case cc.StatementSelection: // SelectionStatement
+		p.selectionStatement(f, n.SelectionStatement)
+	case cc.StatementIteration: // IterationStatement
+		p.iterationStatement(f, n.IterationStatement)
+	case cc.StatementJump: // JumpStatement
+		r = p.jumpStatement(f, n.JumpStatement)
+	case cc.StatementAsm: // AsmStatement
+		// AsmStatement:
+		//         Asm AttributeSpecifierList ';'
+		// Asm:
+		//         "__asm__" AsmQualifierList '(' STRINGLITERAL AsmArgList ')'
+		if n.AsmStatement.Asm.Token3.Value == 0 && n.AsmStatement.Asm.AsmArgList == nil {
+			break
+		}
+
+		p.w("panic(`%s: assembler statements not supported`)", n.Position())
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+	if forceCompoundStmtBrace {
+		p.w("}")
+	}
+	return r
+}
+
+func (p *project) jumpStatement(f *function, n *cc.JumpStatement) (r *cc.JumpStatement) {
+	p.w("%s", tidyComment("\n", n))
+	if _, ok := n.Context().(*cc.SelectionStatement); ok && f.ifCtx == nil {
+		switch f.switchCtx {
+		case inSwitchCase:
+			f.switchCtx = inSwitchSeenBreak
+		case inSwitchSeenBreak:
+			// nop but TODO
+		case inSwitchFlat:
+			// ok
+		default:
+			panic(todo("", n.Position(), f.switchCtx))
+		}
+	}
+
+	switch n.Case {
+	case cc.JumpStatementGoto: // "goto" IDENTIFIER ';'
+		p.w("goto %s", f.labelNames[n.Token2.Value])
+	case cc.JumpStatementGotoExpr: // "goto" '*' Expression ';'
+		panic(todo("", p.pos(n)))
+	case cc.JumpStatementContinue: // "continue" ';'
+		switch {
+		case f.continueCtx != 0:
+			p.w("goto __%d", f.continueCtx)
+		default:
+			p.w("continue")
+		}
+	case cc.JumpStatementBreak: // "break" ';'
+		switch {
+		case f.breakCtx != 0:
+			p.w("goto __%d", f.breakCtx)
+		default:
+			p.w("break")
+		}
+	case cc.JumpStatementReturn: // "return" Expression ';'
+		r = n
+		switch {
+		case f.rt != nil && f.rt.Kind() == cc.Void:
+			if n.Expression != nil {
+				p.expression(f, n.Expression, n.Expression.Operand.Type(), exprVoid, 0)
+				p.w(";")
+			}
+			p.w("return")
+		case f.rt != nil && f.rt.Kind() != cc.Void:
+			if n.Expression != nil {
+				p.expression(f, n.Expression, f.rt, exprCondReturn, 0)
+				break
+			}
+
+			p.w("return ")
+			p.zeroValue(n, f.rt)
+		default:
+			if n.Expression != nil {
+				p.expression(f, n.Expression, n.Expression.Operand.Type(), exprVoid, 0)
+				p.w(";")
+			}
+			p.w("return")
+		}
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+	return r
+}
+
+func (p *project) expression(f *function, n *cc.Expression, t cc.Type, mode exprMode, flags flags) {
+	switch mode {
+	case exprVoid:
+		p.expressionVoid(f, n, t, mode, flags)
+	case exprValue, exprCondReturn, exprCondInit:
+		p.expressionValue(f, n, t, mode, flags)
+	case exprBool:
+		p.expressionBool(f, n, t, mode, flags)
+	case exprAddrOf:
+		p.expressionAddrOf(f, n, t, mode, flags)
+	case exprPSelect:
+		p.expressionPSelect(f, n, t, mode, flags)
+	case exprLValue:
+		p.expressionLValue(f, n, t, mode, flags)
+	case exprFunc:
+		p.expressionFunc(f, n, t, mode, flags)
+	case exprSelect:
+		p.expressionSelect(f, n, t, mode, flags)
+	case exprDecay:
+		p.expressionDecay(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), mode))
+	}
+}
+
+func (p *project) expressionDecay(f *function, n *cc.Expression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ExpressionAssign: // AssignmentExpression
+		p.assignmentExpression(f, n.AssignmentExpression, t, mode, flags)
+	case cc.ExpressionComma: // Expression ',' AssignmentExpression
+		p.w("func() uintptr {")
+		p.expression(f, n.Expression, n.Expression.Operand.Type(), exprVoid, flags)
+		p.w("; return ")
+		p.assignmentExpression(f, n.AssignmentExpression, t, mode, flags)
+		p.w("}()")
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) expressionSelect(f *function, n *cc.Expression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ExpressionAssign: // AssignmentExpression
+		p.assignmentExpression(f, n.AssignmentExpression, t, mode, flags)
+	case cc.ExpressionComma: // Expression ',' AssignmentExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) expressionFunc(f *function, n *cc.Expression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ExpressionAssign: // AssignmentExpression
+		p.assignmentExpression(f, n.AssignmentExpression, t, mode, flags)
+	case cc.ExpressionComma: // Expression ',' AssignmentExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) expressionLValue(f *function, n *cc.Expression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ExpressionAssign: // AssignmentExpression
+		p.assignmentExpression(f, n.AssignmentExpression, t, mode, flags)
+	case cc.ExpressionComma: // Expression ',' AssignmentExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) expressionPSelect(f *function, n *cc.Expression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ExpressionAssign: // AssignmentExpression
+		p.assignmentExpression(f, n.AssignmentExpression, t, mode, flags)
+	case cc.ExpressionComma: // Expression ',' AssignmentExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) expressionAddrOf(f *function, n *cc.Expression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ExpressionAssign: // AssignmentExpression
+		p.assignmentExpression(f, n.AssignmentExpression, t, mode, flags)
+	case cc.ExpressionComma: // Expression ',' AssignmentExpression
+		p.w(" func() uintptr {")
+		p.expression(f, n.Expression, n.Expression.Operand.Type(), exprVoid, flags)
+		p.w("; return ")
+		p.assignmentExpression(f, n.AssignmentExpression, t, mode, flags)
+		p.w("}()")
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) expressionBool(f *function, n *cc.Expression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ExpressionAssign: // AssignmentExpression
+		p.assignmentExpression(f, n.AssignmentExpression, t, mode, flags)
+	case cc.ExpressionComma: // Expression ',' AssignmentExpression
+		p.w("func() bool {")
+		p.expression(f, n.Expression, n.Expression.Operand.Type(), exprVoid, flags)
+		p.w("; return ")
+		p.assignmentExpression(f, n.AssignmentExpression, n.AssignmentExpression.Operand.Type(), mode, flags)
+		p.w("}()")
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) expressionValue(f *function, n *cc.Expression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ExpressionAssign: // AssignmentExpression
+		p.assignmentExpression(f, n.AssignmentExpression, t, mode, flags)
+	case cc.ExpressionComma: // Expression ',' AssignmentExpression
+		if mode == exprCondReturn {
+			p.expression(f, n.Expression, n.Expression.Operand.Type(), exprVoid, flags)
+			p.w("; return ")
+			p.assignmentExpression(f, n.AssignmentExpression, n.AssignmentExpression.Operand.Type(), exprValue, flags)
+			return
+		}
+
+		switch {
+		case n.AssignmentExpression.Operand.Type().Kind() == cc.Array:
+			p.expressionDecay(f, n, t, exprDecay, flags)
+		default:
+			defer p.w("%s", p.convertType(n, n.Operand.Type(), t, flags))
+			p.w("func() %v {", p.typ(n, n.AssignmentExpression.Operand.Type()))
+			p.expression(f, n.Expression, n.Expression.Operand.Type(), exprVoid, flags)
+			p.w("; return ")
+			p.assignmentExpression(f, n.AssignmentExpression, n.AssignmentExpression.Operand.Type(), exprValue, flags)
+			p.w("}()")
+		}
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) expressionVoid(f *function, n *cc.Expression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ExpressionAssign: // AssignmentExpression
+		p.assignmentExpression(f, n.AssignmentExpression, t, mode, flags)
+	case cc.ExpressionComma: // Expression ',' AssignmentExpression
+		p.expression(f, n.Expression, t, mode, flags)
+		p.w(";")
+		p.assignmentExpression(f, n.AssignmentExpression, t, mode, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) opKind(f *function, d declarator, t cc.Type) opKind {
+	switch {
+	case p.isArrayParameter(d, t):
+		return opArrayParameter
+	case !p.pass1 && p.isArray(f, d, t):
+		return opArray
+	case t.Kind() == cc.Union:
+		return opUnion
+	case t.Kind() == cc.Struct:
+		return opStruct
+	case t.IsBitFieldType():
+		return opBitfield
+	case t.Kind() == cc.Function:
+		return opFunction
+	default:
+		return opNormal
+	}
+}
+
+func (p *project) assignmentExpression(f *function, n *cc.AssignmentExpression, t cc.Type, mode exprMode, flags flags) {
+	switch mode {
+	case exprVoid:
+		p.assignmentExpressionVoid(f, n, t, mode, flags)
+	case exprValue, exprCondReturn, exprCondInit:
+		p.assignmentExpressionValue(f, n, t, mode, flags)
+	case exprAddrOf:
+		p.assignmentExpressionAddrOf(f, n, t, mode, flags)
+	case exprBool:
+		p.assignmentExpressionBool(f, n, t, mode, flags)
+	case exprLValue:
+		p.assignmentExpressionLValue(f, n, t, mode, flags)
+	case exprPSelect:
+		p.assignmentExpressionPSelect(f, n, t, mode, flags)
+	case exprFunc:
+		p.assignmentExpressionFunc(f, n, t, mode, flags)
+	case exprSelect:
+		p.assignmentExpressionSelect(f, n, t, mode, flags)
+	case exprDecay:
+		p.assignmentExpressionDecay(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), mode))
+	}
+}
+
+func (p *project) assignmentExpressionDecay(f *function, n *cc.AssignmentExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.AssignmentExpressionCond: // ConditionalExpression
+		p.conditionalExpression(f, n.ConditionalExpression, t, mode, flags)
+	case cc.AssignmentExpressionAssign: // UnaryExpression '=' AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionMul: // UnaryExpression "*=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionDiv: // UnaryExpression "/=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionMod: // UnaryExpression "%=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionAdd: // UnaryExpression "+=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionSub: // UnaryExpression "-=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionLsh: // UnaryExpression "<<= AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionRsh: // UnaryExpression ">>=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionAnd: // UnaryExpression "&=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionXor: // UnaryExpression "^=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionOr: // UnaryExpression "|=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) assignmentExpressionSelect(f *function, n *cc.AssignmentExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.AssignmentExpressionCond: // ConditionalExpression
+		p.conditionalExpression(f, n.ConditionalExpression, t, mode, flags)
+	case cc.AssignmentExpressionAssign: // UnaryExpression '=' AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionMul: // UnaryExpression "*=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionDiv: // UnaryExpression "/=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionMod: // UnaryExpression "%=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionAdd: // UnaryExpression "+=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionSub: // UnaryExpression "-=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionLsh: // UnaryExpression "<<=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionRsh: // UnaryExpression ">>=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionAnd: // UnaryExpression "&=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionXor: // UnaryExpression "^=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionOr: // UnaryExpression "|=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) assignmentExpressionFunc(f *function, n *cc.AssignmentExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.AssignmentExpressionCond: // ConditionalExpression
+		p.conditionalExpression(f, n.ConditionalExpression, t, mode, flags)
+	case cc.AssignmentExpressionAssign: // UnaryExpression '=' AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionMul: // UnaryExpression "*=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionDiv: // UnaryExpression "/=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionMod: // UnaryExpression "%=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionAdd: // UnaryExpression "+=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionSub: // UnaryExpression "-=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionLsh: // UnaryExpremode, ssion "<<=
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionRsh: // UnaryExpression ">>=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionAnd: // UnaryExpression "&=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionXor: // UnaryExpression "^=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionOr: // UnaryExpression "|=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) assignmentExpressionPSelect(f *function, n *cc.AssignmentExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.AssignmentExpressionCond: // ConditionalExpression
+		p.conditionalExpression(f, n.ConditionalExpression, t, mode, flags)
+	case cc.AssignmentExpressionAssign: // UnaryExpression '=' AssignmentExpression
+		p.w("(*%s)(unsafe.Pointer(", p.typ(n, n.AssignmentExpression.Operand.Type().Elem()))
+		p.assignmentExpression(f, n, t, exprValue, flags)
+		p.w("))")
+	case cc.AssignmentExpressionMul: // UnaryExpression "*=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionDiv: // UnaryExpression "/=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionMod: // UnaryExpression "%=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionAdd: // UnaryExpression "+=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionSub: // UnaryExpression "-=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionLsh: // UnaryExpression "<<=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionRsh: // UnaryExpression ">>=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionAnd: // UnaryExpression "&=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionXor: // UnaryExpression "^=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionOr: // UnaryExpression "|=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) assignmentExpressionLValue(f *function, n *cc.AssignmentExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.AssignmentExpressionCond: // ConditionalExpression
+		p.conditionalExpression(f, n.ConditionalExpression, t, mode, flags)
+	case cc.AssignmentExpressionAssign: // UnaryExpression '=' AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionMul: // UnaryExpression "*=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionDiv: // UnaryExpression "/=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionMod: // UnaryExpression "%=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionAdd: // UnaryExpression "+=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionSub: // UnaryExpression "-=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionLsh: // UnaryExpression "<<=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionRsh: // UnaryExpression ">>=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionAnd: // UnaryExpression "&=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionXor: // UnaryExpression "^=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionOr: // UnaryExpression "|=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) assignmentExpressionBool(f *function, n *cc.AssignmentExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.AssignmentExpressionCond: // ConditionalExpression
+		p.conditionalExpression(f, n.ConditionalExpression, t, mode, flags)
+	default:
+		// case cc.AssignmentExpressionAssign: // UnaryExpression '=' AssignmentExpression
+		// case cc.AssignmentExpressionMul: // UnaryExpression "*=" AssignmentExpression
+		// case cc.AssignmentExpressionDiv: // UnaryExpression "/=" AssignmentExpression
+		// case cc.AssignmentExpressionMod: // UnaryExpression "%=" AssignmentExpression
+		// case cc.AssignmentExpressionAdd: // UnaryExpression "+=" AssignmentExpression
+		// case cc.AssignmentExpressionSub: // UnaryExpression "-=" AssignmentExpression
+		// case cc.AssignmentExpressionLsh: // UnaryExpression "<<=" AssignmentExpression
+		// case cc.AssignmentExpressionRsh: // UnaryExpression ">>=" AssignmentExpression
+		// case cc.AssignmentExpressionAnd: // UnaryExpression "&=" AssignmentExpression
+		// case cc.AssignmentExpressionXor: // UnaryExpression "^=" AssignmentExpression
+		// case cc.AssignmentExpressionOr: // UnaryExpression "|=" AssignmentExpression
+		p.w("(")
+		defer p.w(")")
+		defer p.w(" != 0 ")
+		p.assignmentExpression(f, n, t, exprValue, flags)
+	}
+}
+
+func (p *project) assignmentExpressionAddrOf(f *function, n *cc.AssignmentExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.AssignmentExpressionCond: // ConditionalExpression
+		p.conditionalExpression(f, n.ConditionalExpression, t, mode, flags)
+	case cc.AssignmentExpressionAssign: // UnaryExpression '=' AssignmentExpression
+		p.assignmentExpressionValueAddrOf(f, n, t, mode, flags)
+	case cc.AssignmentExpressionMul: // UnaryExpression "*=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionDiv: // UnaryExpression "/=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionMod: // UnaryExpression "%=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionAdd: // UnaryExpression "+=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionSub: // UnaryExpression "-=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionLsh: // UnaryExpression "<<=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionRsh: // UnaryExpression ">>=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionAnd: // UnaryExpression "&=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionXor: // UnaryExpression "^=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	case cc.AssignmentExpressionOr: // UnaryExpression "|=" AssignmentExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) assignmentExpressionValueAddrOf(f *function, n *cc.AssignmentExpression, t cc.Type, mode exprMode, flags flags) {
+	// UnaryExpression '=' AssignmentExpression
+	if mode == exprCondReturn {
+		panic(todo("", p.pos(n)))
+	}
+
+	lhs := n.UnaryExpression
+	switch k := p.opKind(f, lhs, lhs.Operand.Type()); k {
+	case opStruct, opUnion:
+		p.assignmentExpressionValueAssignStructAddrof(f, n, n.Operand.Type(), mode, flags)
+	default:
+		panic(todo("", n.Position(), k))
+	}
+}
+
+func (p *project) assignmentExpressionValueAssignStructAddrof(f *function, n *cc.AssignmentExpression, t cc.Type, mode exprMode, flags flags) {
+	// UnaryExpression '=' AssignmentExpression
+	lhs := n.UnaryExpression.Operand.Type()
+	rhs := n.AssignmentExpression.Operand.Type()
+	if lhs.Kind() == cc.Array || rhs.Kind() == cc.Array {
+		panic(todo("", p.pos(n)))
+	}
+
+	if d := n.UnaryExpression.Declarator(); d != nil {
+		if local := f.locals[d]; local != nil {
+			if local.isPinned {
+				if !p.pass1 {
+					p.w("%sXmemmove(tls, ", p.task.crt)
+					p.unaryExpression(f, n.UnaryExpression, lhs, exprAddrOf, flags)
+					p.w(", ")
+					p.assignmentExpression(f, n.AssignmentExpression, rhs, exprAddrOf, flags)
+					p.w(", %d)", lhs.Size())
+					return
+				}
+			}
+
+			if !p.pass1 {
+				panic(todo("", p.pos(n)))
+			}
+		}
+	}
+
+	p.w("%sXmemmove(tls, ", p.task.crt)
+	p.unaryExpression(f, n.UnaryExpression, lhs, exprAddrOf, flags)
+	p.w(", ")
+	p.assignmentExpression(f, n.AssignmentExpression, rhs, exprAddrOf, flags)
+	p.w(", %d)", lhs.Size())
+}
+
+func (p *project) assignmentExpressionValue(f *function, n *cc.AssignmentExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.AssignmentExpressionCond: // ConditionalExpression
+		p.conditionalExpression(f, n.ConditionalExpression, t, mode, flags)
+	case cc.AssignmentExpressionAssign: // UnaryExpression '=' AssignmentExpression
+		p.assignmentExpressionValueAssign(f, n, t, mode, flags)
+	case cc.AssignmentExpressionMul: // UnaryExpression "*=" AssignmentExpression
+		p.assignOp(f, n, t, mode, "*", "Mul", flags)
+	case cc.AssignmentExpressionDiv: // UnaryExpression "/=" AssignmentExpression
+		p.assignOp(f, n, t, mode, "/", "Div", flags)
+	case cc.AssignmentExpressionMod: // UnaryExpression "%=" AssignmentExpression
+		p.assignOp(f, n, t, mode, "%", "Rem", flags)
+	case cc.AssignmentExpressionAdd: // UnaryExpression "+=" AssignmentExpression
+		p.assignOp(f, n, t, mode, "+", "Add", flags)
+	case cc.AssignmentExpressionSub: // UnaryExpression "-=" AssignmentExpression
+		p.assignOp(f, n, t, mode, "-", "Sub", flags)
+	case cc.AssignmentExpressionLsh: // UnaryExpremode, ssion "<<=
+		p.assignOp(f, n, t, mode, "<<", "Shl", flags)
+	case cc.AssignmentExpressionRsh: // UnaryExpression ">>=" AssignmentExpression
+		p.assignOp(f, n, t, mode, ">>", "Shr", flags)
+	case cc.AssignmentExpressionAnd: // UnaryExpression "&=" AssignmentExpression
+		p.assignOp(f, n, t, mode, "&", "And", flags)
+	case cc.AssignmentExpressionXor: // UnaryExpression "^=" AssignmentExpression
+		p.assignOp(f, n, t, mode, "^", "Xor", flags)
+	case cc.AssignmentExpressionOr: // UnaryExpression "|=" AssignmentExpression
+		p.assignOp(f, n, t, mode, "|", "Or", flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) assignmentExpressionValueAssign(f *function, n *cc.AssignmentExpression, t cc.Type, mode exprMode, flags flags) {
+	// UnaryExpression '=' AssignmentExpression
+	if mode == exprCondReturn {
+		p.w("return ")
+	}
+	lhs := n.UnaryExpression
+	switch k := p.opKind(f, lhs, lhs.Operand.Type()); k {
+	case opNormal:
+		p.assignmentExpressionValueAssignNormal(f, n, t, mode, flags)
+	case opBitfield:
+		p.assignmentExpressionValueAssignBitfield(f, n, t, mode, flags)
+	case opStruct, opUnion:
+		p.assignmentExpressionValueAssignStruct(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), k))
+	}
+}
+
+func (p *project) assignmentExpressionValueAssignStruct(f *function, n *cc.AssignmentExpression, t cc.Type, mode exprMode, flags flags) {
+	// UnaryExpression '=' AssignmentExpression
+	lhs := n.UnaryExpression.Operand.Type()
+	rhs := n.AssignmentExpression.Operand.Type()
+	if lhs.Kind() == cc.Array || rhs.Kind() == cc.Array {
+		panic(todo("", p.pos(n)))
+	}
+
+	p.w(" func() %s { __v := ", p.typ(n, lhs))
+	p.assignmentExpression(f, n.AssignmentExpression, rhs, exprValue, flags)
+	p.w(";")
+	p.unaryExpression(f, n.UnaryExpression, lhs, exprLValue, flags)
+	p.w(" = __v; return __v}()")
+}
+
+func (p *project) assignmentExpressionValueAssignBitfield(f *function, n *cc.AssignmentExpression, t cc.Type, mode exprMode, flags flags) {
+	if d := n.UnaryExpression.Declarator(); d != nil {
+		panic(todo("", p.pos(n)))
+	}
+
+	lhs := n.UnaryExpression
+	lt := lhs.Operand.Type()
+	bf := lt.BitField()
+	defer p.w("%s", p.convertType(n, lt, t, flags))
+	p.w("%sAssignBitFieldPtr%d%s(", p.task.crt, bf.BitFieldBlockWidth(), p.bfHelperType(lt))
+	p.unaryExpression(f, lhs, lt, exprAddrOf, flags)
+	p.w(", ")
+	p.assignmentExpression(f, n.AssignmentExpression, lt, exprValue, flags)
+	p.w(", %d, %d, %#x)", bf.BitFieldWidth(), bf.BitFieldOffset(), bf.Mask())
+}
+
+func (p *project) assignmentExpressionValueAssignNormal(f *function, n *cc.AssignmentExpression, t cc.Type, mode exprMode, flags flags) {
+	if d := n.UnaryExpression.Declarator(); d != nil {
+		if !d.Type().IsScalarType() {
+			panic(todo("", p.pos(n)))
+		}
+
+		if local := f.locals[d]; local != nil {
+			if local.isPinned {
+				defer p.w(")%s", p.convertType(n, d.Type(), t, flags))
+				p.w("%sAssignPtr%s(", p.task.crt, p.helperType(d, d.Type()))
+				p.w("%s%s /* %s */", f.bpName, nonZeroUintptr(local.off), local.name)
+				p.w(", ")
+				p.assignmentExpression(f, n.AssignmentExpression, n.UnaryExpression.Operand.Type(), exprValue, flags)
+				return
+			}
+
+			defer p.w(")%s", p.convertType(n, d.Type(), t, flags))
+			p.w("%sAssign%s(&%s, ", p.task.crt, p.helperType(n, d.Type()), local.name)
+			p.assignmentExpression(f, n.AssignmentExpression, n.UnaryExpression.Operand.Type(), exprValue, flags)
+			return
+		}
+	}
+
+	defer p.w(")%s", p.convertType(n, n.UnaryExpression.Operand.Type(), t, flags))
+	p.w("%sAssignPtr%s(", p.task.crt, p.helperType(n, n.UnaryExpression.Operand.Type()))
+	p.unaryExpression(f, n.UnaryExpression, n.UnaryExpression.Operand.Type(), exprAddrOf, flags)
+	p.w(", ")
+	p.assignmentExpression(f, n.AssignmentExpression, n.UnaryExpression.Operand.Type(), exprValue, flags)
+}
+
+func (p *project) assignmentExpressionVoid(f *function, n *cc.AssignmentExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.AssignmentExpressionCond: // ConditionalExpression
+		p.conditionalExpression(f, n.ConditionalExpression, t, mode, flags)
+	case cc.AssignmentExpressionAssign: // UnaryExpression '=' AssignmentExpression
+		d := n.UnaryExpression.Declarator()
+		lhs := n.UnaryExpression
+		lt := lhs.Operand.Type()
+		sv := f.condInitPrefix
+		switch k := p.opKind(f, lhs, lt); k {
+		case opArrayParameter:
+			lt = lt.Decay()
+			fallthrough
+		case opNormal, opStruct:
+			mode = exprValue
+			if p.isArrayOrPinnedArray(f, n.AssignmentExpression, n.AssignmentExpression.Operand.Type()) {
+				mode = exprDecay
+			}
+			switch {
+			case flags&fNoCondAssignment == 0 && mode == exprValue && n.UnaryExpression.Declarator() != nil && p.isConditionalAssignmentExpr(n.AssignmentExpression):
+				f.condInitPrefix = func() {
+					p.unaryExpression(f, lhs, lt, exprLValue, flags)
+					p.w(" = ")
+				}
+				p.assignmentExpression(f, n.AssignmentExpression, lt, exprCondInit, flags)
+				p.w(";")
+			default:
+				if d != nil && p.isVolatileOrAtomic(d) {
+					p.setVolatileDeclarator(d, f, n.AssignmentExpression, lt, mode, flags)
+					return
+				}
+
+				p.unaryExpression(f, lhs, lt, exprLValue, flags)
+				p.w(" = ")
+				p.assignmentExpression(f, n.AssignmentExpression, lt, mode, flags)
+			}
+		case opBitfield:
+			bf := lt.BitField()
+			p.w("%sSetBitFieldPtr%d%s(", p.task.crt, bf.BitFieldBlockWidth(), p.bfHelperType(lt))
+			p.unaryExpression(f, lhs, lt, exprAddrOf, flags)
+			p.w(", ")
+			p.assignmentExpression(f, n.AssignmentExpression, lt, exprValue, flags)
+			p.w(", %d, %#x)", bf.BitFieldOffset(), bf.Mask())
+		case opUnion:
+			p.unaryExpression(f, lhs, lt, exprLValue, flags)
+			p.w(" = ")
+			p.assignmentExpression(f, n.AssignmentExpression, lt, exprValue, flags)
+		default:
+			panic(todo("", n.Position(), k))
+		}
+		f.condInitPrefix = sv
+	case cc.AssignmentExpressionMul: // UnaryExpression "*=" AssignmentExpression
+		p.assignOp(f, n, t, mode, "*", "Mul", flags)
+	case cc.AssignmentExpressionDiv: // UnaryExpression "/=" AssignmentExpression
+		p.assignOp(f, n, t, mode, "/", "Div", flags)
+	case cc.AssignmentExpressionMod: // UnaryExpression "%=" AssignmentExpression
+		p.assignOp(f, n, t, mode, "%", "Mod", flags)
+	case cc.AssignmentExpressionAdd: // UnaryExpression "+=" AssignmentExpression
+		p.assignOp(f, n, t, mode, "+", "Add", flags)
+	case cc.AssignmentExpressionSub: // UnaryExpression "-=" AssignmentExpression
+		p.assignOp(f, n, t, mode, "-", "Sub", flags)
+	case cc.AssignmentExpressionLsh: // UnaryExpression "<<=" AssignmentExpression
+		p.assignShiftOp(f, n, t, mode, "<<", "Shl", flags)
+	case cc.AssignmentExpressionRsh: // UnaryExpression ">>=" AssignmentExpression
+		p.assignShiftOp(f, n, t, mode, ">>", "Shr", flags)
+	case cc.AssignmentExpressionAnd: // UnaryExpression "&=" AssignmentExpression
+		p.assignOp(f, n, t, mode, "&", "And", flags)
+	case cc.AssignmentExpressionXor: // UnaryExpression "^=" AssignmentExpression
+		p.assignOp(f, n, t, mode, "^", "Xor", flags)
+	case cc.AssignmentExpressionOr: // UnaryExpression "|=" AssignmentExpression
+		p.assignOp(f, n, t, mode, "|", "Or", flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) setVolatileDeclarator(d *cc.Declarator, f *function, n *cc.AssignmentExpression, t cc.Type, mode exprMode, flags flags) {
+	sz := d.Type().Size()
+	switch sz {
+	case 4, 8:
+		// ok
+	default:
+		p.err(d, "unsupported volatile declarator size: %v", sz)
+		return
+	}
+
+	if local := f.locals[d]; local != nil {
+		if local.isPinned {
+			p.w("%sAtomicStoreP%s(%s%s /* %s */, ", p.task.crt, p.helperType(n, d.Type()), f.bpName, nonZeroUintptr(local.off), local.name)
+			p.assignmentExpression(f, n, t, mode, flags)
+			p.w(")")
+			return
+		}
+
+		p.atomicStoreNamedAddr(n, d.Type(), local.name, n, f, mode, flags)
+		return
+	}
+
+	if tld := p.tlds[d]; tld != nil {
+		p.atomicStoreNamedAddr(n, d.Type(), tld.name, n, f, mode, flags)
+		return
+	}
+
+	if imp := p.imports[d.Name().String()]; imp != nil {
+		p.atomicStoreNamedAddr(n, d.Type(), fmt.Sprintf("%sX%s", imp.qualifier, d.Name()), n, f, mode, flags)
+		return
+	}
+
+	panic(todo("", n.Position(), d.Position(), d.Name()))
+}
+
+func (p *project) atomicStoreNamedAddr(n cc.Node, t cc.Type, nm string, expr *cc.AssignmentExpression, f *function, mode exprMode, flags flags) {
+	sz := t.Size()
+	switch sz {
+	case 4, 8:
+		// ok
+	default:
+		p.err(n, "unsupported volatile declarator size: %v", sz)
+		return
+	}
+
+	var ht string
+	switch {
+	case t.IsScalarType():
+		ht = p.helperType(n, t)
+	default:
+		p.err(n, "unsupported volatile declarator type: %v", t)
+		return
+	}
+
+	p.w("%sAtomicStore%s(&%s, %s(", p.task.crt, ht, nm, p.typ(n, t))
+	p.assignmentExpression(f, expr, t, mode, flags)
+	p.w("))")
+}
+
+func (p *project) atomicLoadNamedAddr(n cc.Node, t cc.Type, nm string) {
+	sz := t.Size()
+	switch sz {
+	case 4, 8:
+		// ok
+	default:
+		p.err(n, "unsupported volatile declarator size: %v", sz)
+		return
+	}
+
+	var ht string
+	switch {
+	case t.IsScalarType():
+		ht = p.helperType(n, t)
+	default:
+		p.err(n, "unsupported volatile declarator type: %v", t)
+		return
+	}
+
+	p.w("%sAtomicLoad%s(&%s)", p.task.crt, ht, nm)
+}
+
+func isRealType(op cc.Operand) bool {
+	switch op.Type().Kind() {
+	case cc.Float, cc.Double:
+		return true
+	default:
+		return false
+	}
+}
+
+func (p *project) bfHelperType(t cc.Type) string {
+	switch {
+	case t.IsSignedType():
+		return fmt.Sprintf("Int%d", t.Size()*8)
+	default:
+		return fmt.Sprintf("Uint%d", t.Size()*8)
+	}
+}
+
+func (p *project) helperType(n cc.Node, t cc.Type) string {
+	for t.IsAliasType() {
+		if t2 := t.Alias(); t2 != t { //TODO HDF5 H5O.c
+			t = t2
+			continue
+		}
+
+		break
+	}
+	switch t.Kind() {
+	case cc.Int128:
+		return "Int128"
+	case cc.UInt128:
+		return "Uint128"
+	}
+
+	s := p.typ(n, t)
+	return strings.ToUpper(s[:1]) + s[1:]
+}
+
+func (p *project) helperType2(n cc.Node, from, to cc.Type) string {
+	if from.Kind() == to.Kind() {
+		return fmt.Sprintf("%s%s", p.task.crt, p.helperType(n, from))
+	}
+
+	return fmt.Sprintf("%s%sFrom%s", p.task.crt, p.helperType(n, to), p.helperType(n, from))
+}
+
+func (p *project) conditionalExpression(f *function, n *cc.ConditionalExpression, t cc.Type, mode exprMode, flags flags) {
+	switch mode {
+	case exprValue:
+		p.conditionalExpressionValue(f, n, t, mode, flags)
+	case exprVoid:
+		p.conditionalExpressionVoid(f, n, t, mode, flags)
+	case exprAddrOf:
+		p.conditionalExpressionAddrOf(f, n, t, mode, flags)
+	case exprBool:
+		p.conditionalExpressionBool(f, n, t, mode, flags)
+	case exprLValue:
+		p.conditionalExpressionLValue(f, n, t, mode, flags)
+	case exprPSelect:
+		p.conditionalExpressionPSelect(f, n, t, mode, flags)
+	case exprFunc:
+		p.conditionalExpressionFunc(f, n, t, mode, flags)
+	case exprSelect:
+		p.conditionalExpressionSelect(f, n, t, mode, flags)
+	case exprCondReturn:
+		p.conditionalExpressionReturn(f, n, t, mode, flags)
+	case exprCondInit:
+		p.conditionalExpressionInit(f, n, t, mode, flags)
+	case exprDecay:
+		p.conditionalExpressionDecay(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), mode))
+	}
+}
+
+func (p *project) conditionalExpressionDecay(f *function, n *cc.ConditionalExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ConditionalExpressionLOr: // LogicalOrExpression
+		p.logicalOrExpression(f, n.LogicalOrExpression, t, mode, flags)
+	case cc.ConditionalExpressionCond: // LogicalOrExpression '?' Expression ':' ConditionalExpression
+		t = t.Decay()
+		p.w(" func() %s { if ", p.typ(n, t))
+		p.logicalOrExpression(f, n.LogicalOrExpression, n.LogicalOrExpression.Operand.Type(), exprBool, flags)
+		p.w(" { return ")
+		switch n.Expression.Operand.Type().Kind() {
+		case cc.Array:
+			p.expression(f, n.Expression, t, exprDecay, flags)
+		case cc.Ptr:
+			panic(todo("", n.Expression.Position(), n.Expression.Operand.Type()))
+		default:
+			panic(todo("", n.Expression.Position(), n.Expression.Operand.Type()))
+		}
+		p.w("}; return ")
+		switch n.ConditionalExpression.Operand.Type().Kind() {
+		case cc.Array:
+			p.conditionalExpression(f, n.ConditionalExpression, t, exprDecay, flags)
+		default:
+			p.conditionalExpression(f, n.ConditionalExpression, t, exprValue, flags)
+		}
+		p.w("}()")
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) conditionalExpressionInit(f *function, n *cc.ConditionalExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ConditionalExpressionLOr: // LogicalOrExpression
+		f.condInitPrefix()
+		p.logicalOrExpression(f, n.LogicalOrExpression, t, exprValue, flags)
+	case cc.ConditionalExpressionCond: // LogicalOrExpression '?' Expression ':' ConditionalExpression
+		t = t.Decay()
+		p.w("if ")
+		p.logicalOrExpression(f, n.LogicalOrExpression, n.LogicalOrExpression.Operand.Type(), exprBool, flags)
+		p.w(" {")
+		p.expression(f, n.Expression, t, mode, flags)
+		p.w("} else { ")
+		p.conditionalExpression(f, n.ConditionalExpression, t, mode, flags)
+		p.w("}")
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) conditionalExpressionReturn(f *function, n *cc.ConditionalExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ConditionalExpressionLOr: // LogicalOrExpression
+		p.w("return ")
+		p.logicalOrExpression(f, n.LogicalOrExpression, t, exprValue, flags)
+	case cc.ConditionalExpressionCond: // LogicalOrExpression '?' Expression ':' ConditionalExpression
+		t = t.Decay()
+		p.w("if ")
+		p.logicalOrExpression(f, n.LogicalOrExpression, n.LogicalOrExpression.Operand.Type(), exprBool, flags)
+		p.w(" {")
+		p.expression(f, n.Expression, t, mode, flags)
+		p.w("}; ")
+		p.conditionalExpression(f, n.ConditionalExpression, t, mode, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) conditionalExpressionSelect(f *function, n *cc.ConditionalExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ConditionalExpressionLOr: // LogicalOrExpression
+		p.logicalOrExpression(f, n.LogicalOrExpression, t, mode, flags)
+	case cc.ConditionalExpressionCond: // LogicalOrExpression '?' Expression ':' ConditionalExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) conditionalExpressionFunc(f *function, n *cc.ConditionalExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ConditionalExpressionLOr: // LogicalOrExpression
+		p.logicalOrExpression(f, n.LogicalOrExpression, t, mode, flags)
+	case cc.ConditionalExpressionCond: // LogicalOrExpression '?' Expression ':' ConditionalExpression
+		switch ot := n.Operand.Type(); ot.Kind() {
+		case cc.Function:
+			if t.Kind() != cc.Function {
+				panic(todo("", n.Position()))
+			}
+		default:
+			panic(todo("", ot.Kind()))
+		}
+
+		p.w(" func() ")
+		p.functionSignature(n, f, t, "")
+		p.w("{ if ")
+		p.logicalOrExpression(f, n.LogicalOrExpression, n.LogicalOrExpression.Operand.Type(), exprBool, flags)
+		p.w(" { return ")
+		switch d := n.Expression.Declarator(); {
+		case d != nil:
+			p.declaratorDefault(n, d)
+		default:
+			panic(todo("", n.Position()))
+		}
+		p.w("}; return ")
+		switch d := n.ConditionalExpression.Declarator(); {
+		case d != nil:
+			p.declaratorDefault(n, d)
+		default:
+			panic(todo("", n.Position()))
+		}
+		p.w("}()")
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) conditionalExpressionPSelect(f *function, n *cc.ConditionalExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ConditionalExpressionLOr: // LogicalOrExpression
+		p.logicalOrExpression(f, n.LogicalOrExpression, t, mode, flags)
+	case cc.ConditionalExpressionCond: // LogicalOrExpression '?' Expression ':' ConditionalExpression
+		p.w("(*%s)(unsafe.Pointer(", p.typ(n, n.Operand.Type().Elem()))
+		p.conditionalExpression(f, n, t, exprValue, flags)
+		p.w("))")
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) conditionalExpressionLValue(f *function, n *cc.ConditionalExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ConditionalExpressionLOr: // LogicalOrExpression
+		p.logicalOrExpression(f, n.LogicalOrExpression, t, mode, flags)
+	case cc.ConditionalExpressionCond: // LogicalOrExpression '?' Expression ':' ConditionalExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) conditionalExpressionBool(f *function, n *cc.ConditionalExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ConditionalExpressionLOr: // LogicalOrExpression
+		p.logicalOrExpression(f, n.LogicalOrExpression, t, mode, flags)
+	case cc.ConditionalExpressionCond: // LogicalOrExpression '?' Expression ':' ConditionalExpression
+		p.w("(")
+		defer p.w(")")
+		defer p.w(" != 0 ")
+		p.conditionalExpression(f, n, t, exprValue, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) conditionalExpressionAddrOf(f *function, n *cc.ConditionalExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ConditionalExpressionLOr: // LogicalOrExpression
+		p.logicalOrExpression(f, n.LogicalOrExpression, t, mode, flags)
+	case cc.ConditionalExpressionCond: // LogicalOrExpression '?' Expression ':' ConditionalExpression
+		t = t.Decay()
+		p.w(" func() %s { if ", p.typ(n, t))
+		p.logicalOrExpression(f, n.LogicalOrExpression, n.LogicalOrExpression.Operand.Type(), exprBool, flags)
+		p.w(" { return ")
+		p.expression(f, n.Expression, t, exprValue, flags)
+		p.w("}; return ")
+		p.conditionalExpression(f, n.ConditionalExpression, t, exprValue, flags)
+		p.w("}()")
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) conditionalExpressionVoid(f *function, n *cc.ConditionalExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ConditionalExpressionLOr: // LogicalOrExpression
+		p.logicalOrExpression(f, n.LogicalOrExpression, t, mode, flags)
+	case cc.ConditionalExpressionCond: // LogicalOrExpression '?' Expression ':' ConditionalExpression
+		switch {
+		case n.Expression.IsSideEffectsFree:
+			p.w("if !(")
+			p.logicalOrExpression(f, n.LogicalOrExpression, n.LogicalOrExpression.Operand.Type(), exprBool, flags)
+			p.w(") {")
+			p.conditionalExpression(f, n.ConditionalExpression, n.ConditionalExpression.Operand.Type(), mode, flags)
+			p.w("}")
+		default:
+			p.w("if ")
+			p.logicalOrExpression(f, n.LogicalOrExpression, n.LogicalOrExpression.Operand.Type(), exprBool, flags)
+			p.w(" {")
+			p.expression(f, n.Expression, n.Expression.Operand.Type(), mode, flags)
+			p.w("} else {")
+			p.conditionalExpression(f, n.ConditionalExpression, n.ConditionalExpression.Operand.Type(), mode, flags)
+			p.w("}")
+		}
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) conditionalExpressionValue(f *function, n *cc.ConditionalExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ConditionalExpressionLOr: // LogicalOrExpression
+		p.logicalOrExpression(f, n.LogicalOrExpression, t, exprValue, flags)
+	case cc.ConditionalExpressionCond: // LogicalOrExpression '?' Expression ':' ConditionalExpression
+		t = t.Decay()
+		p.w(" func() %s { if ", p.typ(n, t))
+		p.logicalOrExpression(f, n.LogicalOrExpression, n.LogicalOrExpression.Operand.Type(), exprBool, flags)
+		p.w(" { return ")
+		p.expression(f, n.Expression, t, exprValue, flags)
+		p.w("}; return ")
+		p.conditionalExpression(f, n.ConditionalExpression, t, exprValue, flags)
+		p.w("}()")
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) logicalOrExpression(f *function, n *cc.LogicalOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch mode {
+	case exprValue:
+		p.logicalOrExpressionValue(f, n, t, mode, flags)
+	case exprVoid:
+		p.logicalOrExpressionVoid(f, n, t, mode, flags)
+	case exprAddrOf:
+		p.logicalOrExpressionAddrOf(f, n, t, mode, flags)
+	case exprBool:
+		p.logicalOrExpressionBool(f, n, t, mode, flags)
+	case exprLValue:
+		p.logicalOrExpressionLValue(f, n, t, mode, flags)
+	case exprPSelect:
+		p.logicalOrExpressionPSelect(f, n, t, mode, flags)
+	case exprFunc:
+		p.logicalOrExpressionFunc(f, n, t, mode, flags)
+	case exprSelect:
+		p.logicalOrExpressionSelect(f, n, t, mode, flags)
+	case exprDecay:
+		p.logicalOrExpressionDecay(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), mode))
+	}
+}
+
+func (p *project) logicalOrExpressionDecay(f *function, n *cc.LogicalOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.LogicalOrExpressionLAnd: // LogicalAndExpression
+		p.logicalAndExpression(f, n.LogicalAndExpression, t, mode, flags)
+	case cc.LogicalOrExpressionLOr: // LogicalOrExpression "||" LogicalAndExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) logicalOrExpressionSelect(f *function, n *cc.LogicalOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.LogicalOrExpressionLAnd: // LogicalAndExpression
+		p.logicalAndExpression(f, n.LogicalAndExpression, t, mode, flags)
+	case cc.LogicalOrExpressionLOr: // LogicalOrExpression "||" LogicalAndExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) logicalOrExpressionFunc(f *function, n *cc.LogicalOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.LogicalOrExpressionLAnd: // LogicalAndExpression
+		p.logicalAndExpression(f, n.LogicalAndExpression, t, mode, flags)
+	case cc.LogicalOrExpressionLOr: // LogicalOrExpression "||" LogicalAndExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) logicalOrExpressionPSelect(f *function, n *cc.LogicalOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.LogicalOrExpressionLAnd: // LogicalAndExpression
+		p.logicalAndExpression(f, n.LogicalAndExpression, t, mode, flags)
+	case cc.LogicalOrExpressionLOr: // LogicalOrExpression "||" LogicalAndExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) logicalOrExpressionLValue(f *function, n *cc.LogicalOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.LogicalOrExpressionLAnd: // LogicalAndExpression
+		p.logicalAndExpression(f, n.LogicalAndExpression, t, mode, flags)
+	case cc.LogicalOrExpressionLOr: // LogicalOrExpression "||" LogicalAndExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) logicalOrExpressionBool(f *function, n *cc.LogicalOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.LogicalOrExpressionLAnd: // LogicalAndExpression
+		p.logicalAndExpression(f, n.LogicalAndExpression, t, mode, flags)
+	case cc.LogicalOrExpressionLOr: // LogicalOrExpression "||" LogicalAndExpression
+		p.binaryLogicalOrExpression(f, n, t, mode, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) logicalOrExpressionAddrOf(f *function, n *cc.LogicalOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.LogicalOrExpressionLAnd: // LogicalAndExpression
+		p.logicalAndExpression(f, n.LogicalAndExpression, t, mode, flags)
+	case cc.LogicalOrExpressionLOr: // LogicalOrExpression "||" LogicalAndExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) logicalOrExpressionVoid(f *function, n *cc.LogicalOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.LogicalOrExpressionLAnd: // LogicalAndExpression
+		p.logicalAndExpression(f, n.LogicalAndExpression, t, mode, flags)
+	case cc.LogicalOrExpressionLOr: // LogicalOrExpression "||" LogicalAndExpression
+		p.w("_ = ")
+		p.logicalOrExpression(f, n, n.Operand.Type(), exprValue, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) logicalOrExpressionValue(f *function, n *cc.LogicalOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.LogicalOrExpressionLAnd: // LogicalAndExpression
+		p.logicalAndExpression(f, n.LogicalAndExpression, t, mode, flags)
+	case cc.LogicalOrExpressionLOr: // LogicalOrExpression "||" LogicalAndExpression
+		p.binaryLogicalOrExpression(f, n, t, mode, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) binaryLogicalOrExpression(f *function, n *cc.LogicalOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch mode {
+	case exprValue:
+		p.binaryLogicalOrExpressionValue(f, n, t, mode, flags)
+	case exprBool:
+		p.binaryLogicalOrExpressionBool(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), mode))
+	}
+}
+
+func (p *project) binaryLogicalOrExpressionBool(f *function, n *cc.LogicalOrExpression, t cc.Type, mode exprMode, flags flags) {
+	defer p.w("%s", p.booleanBinaryExpression(n, n.Operand, n.Operand.Type(), &mode, flags))
+	p.logicalOrExpression(f, n.LogicalOrExpression, n.LogicalOrExpression.Operand.Type(), exprBool, flags)
+	p.w(" ||%s", tidyComment(" ", &n.Token))
+	p.logicalAndExpression(f, n.LogicalAndExpression, n.LogicalAndExpression.Operand.Type(), exprBool, flags)
+}
+
+func (p *project) binaryLogicalOrExpressionValue(f *function, n *cc.LogicalOrExpression, t cc.Type, mode exprMode, flags flags) {
+	defer p.w("%s", p.booleanBinaryExpression(n, n.Operand, t, &mode, flags))
+	p.logicalOrExpression(f, n.LogicalOrExpression, n.LogicalOrExpression.Operand.Type(), exprBool, flags)
+	p.w(" ||%s", tidyComment(" ", &n.Token))
+	p.logicalAndExpression(f, n.LogicalAndExpression, n.LogicalAndExpression.Operand.Type(), exprBool, flags)
+}
+
+func (p *project) booleanBinaryExpression(n cc.Node, from cc.Operand, to cc.Type, mode *exprMode, flags flags) (r string) {
+	p.w("(")
+	r = ")"
+	switch *mode {
+	case exprBool:
+		*mode = exprValue
+	default:
+		r = p.convert(n, from, to, flags) + r
+		p.w("%sBool32(", p.task.crt)
+		r = ")" + r
+	}
+	return r
+}
+
+func (p *project) logicalAndExpression(f *function, n *cc.LogicalAndExpression, t cc.Type, mode exprMode, flags flags) {
+	switch mode {
+	case exprValue:
+		p.logicalAndExpressionValue(f, n, t, mode, flags)
+	case exprVoid:
+		p.logicalAndExpressionVoid(f, n, t, mode, flags)
+	case exprAddrOf:
+		p.logicalAndExpressionAddrOf(f, n, t, mode, flags)
+	case exprBool:
+		p.logicalAndExpressionBool(f, n, t, mode, flags)
+	case exprLValue:
+		p.logicalAndExpressionLValue(f, n, t, mode, flags)
+	case exprPSelect:
+		p.logicalAndExpressionPSelect(f, n, t, mode, flags)
+	case exprFunc:
+		p.logicalAndExpressionFunc(f, n, t, mode, flags)
+	case exprSelect:
+		p.logicalAndExpressionSelect(f, n, t, mode, flags)
+	case exprDecay:
+		p.logicalAndExpressionDecay(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), mode))
+	}
+}
+
+func (p *project) logicalAndExpressionDecay(f *function, n *cc.LogicalAndExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.LogicalAndExpressionOr: // InclusiveOrExpression
+		p.inclusiveOrExpression(f, n.InclusiveOrExpression, t, mode, flags)
+	case cc.LogicalAndExpressionLAnd: // LogicalAndExpression "&&" InclusiveOrExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) logicalAndExpressionSelect(f *function, n *cc.LogicalAndExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.LogicalAndExpressionOr: // InclusiveOrExpression
+		p.inclusiveOrExpression(f, n.InclusiveOrExpression, t, mode, flags)
+	case cc.LogicalAndExpressionLAnd: // LogicalAndExpression "&&" InclusiveOrExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) logicalAndExpressionFunc(f *function, n *cc.LogicalAndExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.LogicalAndExpressionOr: // InclusiveOrExpression
+		p.inclusiveOrExpression(f, n.InclusiveOrExpression, t, mode, flags)
+	case cc.LogicalAndExpressionLAnd: // LogicalAndExpression "&&" InclusiveOrExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) logicalAndExpressionPSelect(f *function, n *cc.LogicalAndExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.LogicalAndExpressionOr: // InclusiveOrExpression
+		p.inclusiveOrExpression(f, n.InclusiveOrExpression, t, mode, flags)
+	case cc.LogicalAndExpressionLAnd: // LogicalAndExpression "&&" InclusiveOrExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) logicalAndExpressionLValue(f *function, n *cc.LogicalAndExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.LogicalAndExpressionOr: // InclusiveOrExpression
+		p.inclusiveOrExpression(f, n.InclusiveOrExpression, t, mode, flags)
+	case cc.LogicalAndExpressionLAnd: // LogicalAndExpression "&&" InclusiveOrExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) logicalAndExpressionBool(f *function, n *cc.LogicalAndExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.LogicalAndExpressionOr: // InclusiveOrExpression
+		p.inclusiveOrExpression(f, n.InclusiveOrExpression, t, mode, flags)
+	case cc.LogicalAndExpressionLAnd: // LogicalAndExpression "&&" InclusiveOrExpression
+		p.binaryLogicalAndExpression(f, n, t, mode, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) logicalAndExpressionAddrOf(f *function, n *cc.LogicalAndExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.LogicalAndExpressionOr: // InclusiveOrExpression
+		p.inclusiveOrExpression(f, n.InclusiveOrExpression, t, mode, flags)
+	case cc.LogicalAndExpressionLAnd: // LogicalAndExpression "&&" InclusiveOrExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) logicalAndExpressionVoid(f *function, n *cc.LogicalAndExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.LogicalAndExpressionOr: // InclusiveOrExpression
+		p.inclusiveOrExpression(f, n.InclusiveOrExpression, t, mode, flags)
+	case cc.LogicalAndExpressionLAnd: // LogicalAndExpression "&&" InclusiveOrExpression
+		p.binaryLogicalAndExpressionValue(f, n, t, mode, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) logicalAndExpressionValue(f *function, n *cc.LogicalAndExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.LogicalAndExpressionOr: // InclusiveOrExpression
+		p.inclusiveOrExpression(f, n.InclusiveOrExpression, t, mode, flags)
+	case cc.LogicalAndExpressionLAnd: // LogicalAndExpression "&&" InclusiveOrExpression
+		p.binaryLogicalAndExpression(f, n, t, mode, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) binaryLogicalAndExpression(f *function, n *cc.LogicalAndExpression, t cc.Type, mode exprMode, flags flags) {
+	switch mode {
+	case exprBool:
+		p.binaryLogicalAndExpressionBool(f, n, t, mode, flags)
+	case exprValue:
+		p.binaryLogicalAndExpressionValue(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), mode))
+	}
+}
+
+func (p *project) binaryLogicalAndExpressionValue(f *function, n *cc.LogicalAndExpression, t cc.Type, mode exprMode, flags flags) {
+	defer p.w("%s", p.booleanBinaryExpression(n, n.Operand, t, &mode, flags))
+	p.logicalAndExpression(f, n.LogicalAndExpression, n.LogicalAndExpression.Operand.Type(), exprBool, flags)
+	p.w(" &&%s", tidyComment(" ", &n.Token))
+	p.inclusiveOrExpression(f, n.InclusiveOrExpression, n.InclusiveOrExpression.Operand.Type(), exprBool, flags)
+}
+
+func (p *project) binaryLogicalAndExpressionBool(f *function, n *cc.LogicalAndExpression, t cc.Type, mode exprMode, flags flags) {
+	defer p.w("%s", p.booleanBinaryExpression(n, n.Operand, t, &mode, flags))
+	p.logicalAndExpression(f, n.LogicalAndExpression, n.LogicalAndExpression.Operand.Type(), exprBool, flags)
+	p.w(" &&%s", tidyComment(" ", &n.Token))
+	p.inclusiveOrExpression(f, n.InclusiveOrExpression, n.InclusiveOrExpression.Operand.Type(), exprBool, flags)
+}
+
+func (p *project) inclusiveOrExpression(f *function, n *cc.InclusiveOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch mode {
+	case exprValue:
+		p.inclusiveOrExpressionValue(f, n, t, mode, flags)
+	case exprVoid:
+		p.inclusiveOrExpressionVoid(f, n, t, mode, flags)
+	case exprAddrOf:
+		p.inclusiveOrExpressionAddrof(f, n, t, mode, flags)
+	case exprBool:
+		p.inclusiveOrExpressionBool(f, n, t, mode, flags)
+	case exprLValue:
+		p.inclusiveOrExpressionLValue(f, n, t, mode, flags)
+	case exprPSelect:
+		p.inclusiveOrExpressionPSelect(f, n, t, mode, flags)
+	case exprFunc:
+		p.inclusiveOrExpressionFunc(f, n, t, mode, flags)
+	case exprSelect:
+		p.inclusiveOrExpressionSelect(f, n, t, mode, flags)
+	case exprDecay:
+		p.inclusiveOrExpressionDecay(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), mode))
+	}
+}
+
+func (p *project) inclusiveOrExpressionDecay(f *function, n *cc.InclusiveOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.InclusiveOrExpressionXor: // ExclusiveOrExpression
+		p.exclusiveOrExpression(f, n.ExclusiveOrExpression, t, mode, flags)
+	case cc.InclusiveOrExpressionOr: // InclusiveOrExpression '|' ExclusiveOrExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) inclusiveOrExpressionSelect(f *function, n *cc.InclusiveOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.InclusiveOrExpressionXor: // ExclusiveOrExpression
+		p.exclusiveOrExpression(f, n.ExclusiveOrExpression, t, mode, flags)
+	case cc.InclusiveOrExpressionOr: // InclusiveOrExpression '|' ExclusiveOrExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) inclusiveOrExpressionFunc(f *function, n *cc.InclusiveOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.InclusiveOrExpressionXor: // ExclusiveOrExpression
+		p.exclusiveOrExpression(f, n.ExclusiveOrExpression, t, mode, flags)
+	case cc.InclusiveOrExpressionOr: // InclusiveOrExpression '|' ExclusiveOrExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) inclusiveOrExpressionPSelect(f *function, n *cc.InclusiveOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.InclusiveOrExpressionXor: // ExclusiveOrExpression
+		p.exclusiveOrExpression(f, n.ExclusiveOrExpression, t, mode, flags)
+	case cc.InclusiveOrExpressionOr: // InclusiveOrExpression '|' ExclusiveOrExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) inclusiveOrExpressionLValue(f *function, n *cc.InclusiveOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.InclusiveOrExpressionXor: // ExclusiveOrExpression
+		p.exclusiveOrExpression(f, n.ExclusiveOrExpression, t, mode, flags)
+	case cc.InclusiveOrExpressionOr: // InclusiveOrExpression '|' ExclusiveOrExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) inclusiveOrExpressionBool(f *function, n *cc.InclusiveOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.InclusiveOrExpressionXor: // ExclusiveOrExpression
+		p.exclusiveOrExpression(f, n.ExclusiveOrExpression, t, mode, flags)
+	case cc.InclusiveOrExpressionOr: // InclusiveOrExpression '|' ExclusiveOrExpression
+		p.binaryInclusiveOrExpression(f, n, t, mode, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) inclusiveOrExpressionAddrof(f *function, n *cc.InclusiveOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.InclusiveOrExpressionXor: // ExclusiveOrExpression
+		p.exclusiveOrExpression(f, n.ExclusiveOrExpression, t, mode, flags)
+	case cc.InclusiveOrExpressionOr: // InclusiveOrExpression '|' ExclusiveOrExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) inclusiveOrExpressionVoid(f *function, n *cc.InclusiveOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.InclusiveOrExpressionXor: // ExclusiveOrExpression
+		p.exclusiveOrExpression(f, n.ExclusiveOrExpression, t, mode, flags)
+	case cc.InclusiveOrExpressionOr: // InclusiveOrExpression '|' ExclusiveOrExpression
+		p.w("_ = ")
+		p.inclusiveOrExpression(f, n, n.Operand.Type(), exprValue, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) inclusiveOrExpressionValue(f *function, n *cc.InclusiveOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.InclusiveOrExpressionXor: // ExclusiveOrExpression
+		p.exclusiveOrExpression(f, n.ExclusiveOrExpression, t, mode, flags)
+	case cc.InclusiveOrExpressionOr: // InclusiveOrExpression '|' ExclusiveOrExpression
+		p.binaryInclusiveOrExpression(f, n, t, mode, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) binaryInclusiveOrExpression(f *function, n *cc.InclusiveOrExpression, t cc.Type, mode exprMode, flags flags) {
+	// InclusiveOrExpression '|' ExclusiveOrExpression
+	switch mode {
+	case exprBool:
+		p.binaryInclusiveOrExpressionBool(f, n, t, mode, flags)
+	case exprValue:
+		p.binaryInclusiveOrExpressionValue(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), mode))
+	}
+}
+
+func (p *project) binaryInclusiveOrExpressionValue(f *function, n *cc.InclusiveOrExpression, t cc.Type, mode exprMode, flags flags) {
+	// InclusiveOrExpression '|' ExclusiveOrExpression
+
+	lt := n.InclusiveOrExpression.Operand.Type()
+	rt := n.ExclusiveOrExpression.Operand.Type()
+	switch lk, rk := lt.Kind(), rt.Kind(); {
+	case
+		lk == cc.UInt128 || rk == cc.UInt128,
+		lk == cc.Int128 || rk == cc.Int128:
+
+		p.binaryOrExpressionUint128(f, n, t, mode, flags)
+		return
+	}
+
+	defer p.w("%s", p.artithmeticBinaryExpression(n, n.Operand, t, &mode, flags))
+	switch {
+	case orOverflows(n.InclusiveOrExpression.Operand, n.ExclusiveOrExpression.Operand, n.Promote()):
+		p.inclusiveOrExpression(f, n.InclusiveOrExpression, n.Promote(), exprValue, flags)
+		p.w(" |%s", tidyComment(" ", &n.Token))
+		p.exclusiveOrExpression(f, n.ExclusiveOrExpression, n.Promote(), exprValue, flags|fForceRuntimeConv)
+	default:
+		p.inclusiveOrExpression(f, n.InclusiveOrExpression, n.Promote(), exprValue, flags)
+		p.w(" |%s", tidyComment(" ", &n.Token))
+		p.exclusiveOrExpression(f, n.ExclusiveOrExpression, n.Promote(), exprValue, flags)
+	}
+}
+
+func (p *project) binaryOrExpressionUint128(f *function, n *cc.InclusiveOrExpression, t cc.Type, mode exprMode, flags flags) {
+	// InclusiveOrExpression '|' ExclusiveOrExpression
+	defer p.w("%s", p.artithmeticBinaryExpression(n, n.Operand, t, &mode, flags))
+	p.inclusiveOrExpression(f, n.InclusiveOrExpression, n.Promote(), exprValue, flags)
+	p.w(".Or(")
+	p.exclusiveOrExpression(f, n.ExclusiveOrExpression, n.Promote(), exprValue, flags)
+	p.w(")")
+}
+
+func (p *project) binaryInclusiveOrExpressionBool(f *function, n *cc.InclusiveOrExpression, t cc.Type, mode exprMode, flags flags) {
+	defer p.w("%s", p.artithmeticBinaryExpression(n, n.Operand, t, &mode, flags))
+	switch {
+	case orOverflows(n.InclusiveOrExpression.Operand, n.ExclusiveOrExpression.Operand, n.Promote()):
+		p.inclusiveOrExpression(f, n.InclusiveOrExpression, n.Promote(), exprValue, flags)
+		p.w(" |%s", tidyComment(" ", &n.Token))
+		p.exclusiveOrExpression(f, n.ExclusiveOrExpression, n.Promote(), exprValue, flags|fForceRuntimeConv)
+	default:
+		p.inclusiveOrExpression(f, n.InclusiveOrExpression, n.Promote(), exprValue, flags)
+		p.w(" |%s", tidyComment(" ", &n.Token))
+		p.exclusiveOrExpression(f, n.ExclusiveOrExpression, n.Promote(), exprValue, flags)
+	}
+}
+
+func orOverflows(lo, ro cc.Operand, promote cc.Type) bool {
+	a, b, ok := getIntOperands(lo, ro)
+	if !ok {
+		return false
+	}
+
+	return overflows(a.Or(a, b), promote)
+}
+
+func (p *project) artithmeticBinaryExpression(n cc.Node, from cc.Operand, to cc.Type, mode *exprMode, flags flags) (r string) {
+	p.w("(")
+	r = ")"
+	switch *mode {
+	case exprBool:
+		p.w("(")
+		r = ") != 0" + r
+		*mode = exprValue
+	default:
+		switch fk, tk := from.Type().Kind(), to.Kind(); {
+		case fk != tk && fk == cc.Int128:
+			return fmt.Sprintf(".%s()%s", p.helperType(n, to), r)
+		case fk != tk && fk == cc.UInt128:
+			return fmt.Sprintf(".%s()%s", p.helperType(n, to), r)
+		default:
+			r = p.convert(n, from, to, flags) + r
+		}
+	}
+	return r
+}
+
+func (p *project) exclusiveOrExpression(f *function, n *cc.ExclusiveOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch mode {
+	case exprValue:
+		p.exclusiveOrExpressionValue(f, n, t, mode, flags)
+	case exprVoid:
+		p.exclusiveOrExpressionVoid(f, n, t, mode, flags)
+	case exprAddrOf:
+		p.exclusiveOrExpressionAddrOf(f, n, t, mode, flags)
+	case exprBool:
+		p.exclusiveOrExpressionBool(f, n, t, mode, flags)
+	case exprLValue:
+		p.exclusiveOrExpressionLValue(f, n, t, mode, flags)
+	case exprPSelect:
+		p.exclusiveOrExpressionPSelect(f, n, t, mode, flags)
+	case exprFunc:
+		p.exclusiveOrExpressionFunc(f, n, t, mode, flags)
+	case exprSelect:
+		p.exclusiveOrExpressionSelect(f, n, t, mode, flags)
+	case exprDecay:
+		p.exclusiveOrExpressionDecay(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), mode))
+	}
+}
+
+func (p *project) exclusiveOrExpressionDecay(f *function, n *cc.ExclusiveOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ExclusiveOrExpressionAnd: // AndExpression
+		p.andExpression(f, n.AndExpression, t, mode, flags)
+	case cc.ExclusiveOrExpressionXor: // ExclusiveOrExpression '^' AndExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) exclusiveOrExpressionSelect(f *function, n *cc.ExclusiveOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ExclusiveOrExpressionAnd: // AndExpression
+		p.andExpression(f, n.AndExpression, t, mode, flags)
+	case cc.ExclusiveOrExpressionXor: // ExclusiveOrExpression '^' AndExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) exclusiveOrExpressionFunc(f *function, n *cc.ExclusiveOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ExclusiveOrExpressionAnd: // AndExpression
+		p.andExpression(f, n.AndExpression, t, mode, flags)
+	case cc.ExclusiveOrExpressionXor: // ExclusiveOrExpression '^' AndExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) exclusiveOrExpressionPSelect(f *function, n *cc.ExclusiveOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ExclusiveOrExpressionAnd: // AndExpression
+		p.andExpression(f, n.AndExpression, t, mode, flags)
+	case cc.ExclusiveOrExpressionXor: // ExclusiveOrExpression '^' AndExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) exclusiveOrExpressionLValue(f *function, n *cc.ExclusiveOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ExclusiveOrExpressionAnd: // AndExpression
+		p.andExpression(f, n.AndExpression, t, mode, flags)
+	case cc.ExclusiveOrExpressionXor: // ExclusiveOrExpression '^' AndExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) exclusiveOrExpressionBool(f *function, n *cc.ExclusiveOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ExclusiveOrExpressionAnd: // AndExpression
+		p.andExpression(f, n.AndExpression, t, mode, flags)
+	case cc.ExclusiveOrExpressionXor: // ExclusiveOrExpression '^' AndExpression
+		p.binaryExclusiveOrExpression(f, n, t, mode, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) exclusiveOrExpressionAddrOf(f *function, n *cc.ExclusiveOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ExclusiveOrExpressionAnd: // AndExpression
+		p.andExpression(f, n.AndExpression, t, mode, flags)
+	case cc.ExclusiveOrExpressionXor: // ExclusiveOrExpression '^' AndExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) exclusiveOrExpressionVoid(f *function, n *cc.ExclusiveOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ExclusiveOrExpressionAnd: // AndExpression
+		p.andExpression(f, n.AndExpression, t, mode, flags)
+	case cc.ExclusiveOrExpressionXor: // ExclusiveOrExpression '^' AndExpression
+		p.w("_ = ")
+		p.exclusiveOrExpression(f, n, n.Operand.Type(), exprValue, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) exclusiveOrExpressionValue(f *function, n *cc.ExclusiveOrExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ExclusiveOrExpressionAnd: // AndExpression
+		p.andExpression(f, n.AndExpression, t, mode, flags)
+	case cc.ExclusiveOrExpressionXor: // ExclusiveOrExpression '^' AndExpression
+		p.binaryExclusiveOrExpression(f, n, t, mode, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) binaryExclusiveOrExpression(f *function, n *cc.ExclusiveOrExpression, t cc.Type, mode exprMode, flags flags) {
+	// ExclusiveOrExpression '^' AndExpression
+	switch mode {
+	case exprValue, exprBool:
+		p.binaryExclusiveOrExpressionValue(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), mode))
+	}
+}
+
+func (p *project) binaryExclusiveOrExpressionValue(f *function, n *cc.ExclusiveOrExpression, t cc.Type, mode exprMode, flags flags) {
+	// ExclusiveOrExpression '^' AndExpression
+
+	lt := n.ExclusiveOrExpression.Operand.Type()
+	rt := n.AndExpression.Operand.Type()
+	switch lk, rk := lt.Kind(), rt.Kind(); {
+	case
+		lk == cc.UInt128 || rk == cc.UInt128,
+		lk == cc.Int128 || rk == cc.Int128:
+
+		p.binaryExclusiveOrExpressionUint128(f, n, t, mode, flags)
+		return
+	}
+
+	defer p.w("%s", p.artithmeticBinaryExpression(n, n.Operand, t, &mode, flags))
+	switch {
+	case xorOverflows(n.ExclusiveOrExpression.Operand, n.AndExpression.Operand, n.Promote()):
+		p.exclusiveOrExpression(f, n.ExclusiveOrExpression, n.Promote(), exprValue, flags)
+		p.w(" ^%s", tidyComment(" ", &n.Token))
+		p.andExpression(f, n.AndExpression, n.Promote(), exprValue, flags|fForceRuntimeConv)
+	default:
+		p.exclusiveOrExpression(f, n.ExclusiveOrExpression, n.Promote(), exprValue, flags)
+		p.w(" ^%s", tidyComment(" ", &n.Token))
+		p.andExpression(f, n.AndExpression, n.Promote(), exprValue, flags)
+	}
+}
+
+func (p *project) binaryExclusiveOrExpressionUint128(f *function, n *cc.ExclusiveOrExpression, t cc.Type, mode exprMode, flags flags) {
+	// ExclusiveOrExpression '^' AndExpression
+	defer p.w("%s", p.artithmeticBinaryExpression(n, n.Operand, t, &mode, flags))
+	p.exclusiveOrExpression(f, n.ExclusiveOrExpression, n.Promote(), exprValue, flags)
+	p.w(".Xor(")
+	p.andExpression(f, n.AndExpression, n.Promote(), exprValue, flags)
+	p.w(")")
+}
+
+func xorOverflows(lo, ro cc.Operand, promote cc.Type) bool {
+	a, b, ok := getIntOperands(lo, ro)
+	if !ok {
+		return false
+	}
+
+	return !lo.Type().IsSignedType() && a.Sign() == 0 ||
+		!ro.Type().IsSignedType() && b.Sign() == 0 ||
+		overflows(a.Xor(a, b), promote)
+}
+
+func (p *project) andExpression(f *function, n *cc.AndExpression, t cc.Type, mode exprMode, flags flags) {
+	switch mode {
+	case exprValue:
+		p.andExpressionValue(f, n, t, mode, flags)
+	case exprVoid:
+		p.andExpressionVoid(f, n, t, mode, flags)
+	case exprAddrOf:
+		p.andExpressionAddrof(f, n, t, mode, flags)
+	case exprBool:
+		p.andExpressionBool(f, n, t, mode, flags)
+	case exprLValue:
+		p.andExpressionLValue(f, n, t, mode, flags)
+	case exprPSelect:
+		p.andExpressionPSelect(f, n, t, mode, flags)
+	case exprFunc:
+		p.andExpressionFunc(f, n, t, mode, flags)
+	case exprSelect:
+		p.andExpressionSelect(f, n, t, mode, flags)
+	case exprDecay:
+		p.andExpressionDecay(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), mode))
+	}
+}
+
+func (p *project) andExpressionDecay(f *function, n *cc.AndExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.AndExpressionEq: // EqualityExpression
+		p.equalityExpression(f, n.EqualityExpression, t, mode, flags)
+	case cc.AndExpressionAnd: // AndExpression '&' EqualityExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) andExpressionSelect(f *function, n *cc.AndExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.AndExpressionEq: // EqualityExpression
+		p.equalityExpression(f, n.EqualityExpression, t, mode, flags)
+	case cc.AndExpressionAnd: // AndExpression '&' EqualityExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) andExpressionFunc(f *function, n *cc.AndExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.AndExpressionEq: // EqualityExpression
+		p.equalityExpression(f, n.EqualityExpression, t, mode, flags)
+	case cc.AndExpressionAnd: // AndExpression '&' EqualityExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) andExpressionPSelect(f *function, n *cc.AndExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.AndExpressionEq: // EqualityExpression
+		p.equalityExpression(f, n.EqualityExpression, t, mode, flags)
+	case cc.AndExpressionAnd: // AndExpression '&' EqualityExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) andExpressionLValue(f *function, n *cc.AndExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.AndExpressionEq: // EqualityExpression
+		p.equalityExpression(f, n.EqualityExpression, t, mode, flags)
+	case cc.AndExpressionAnd: // AndExpression '&' EqualityExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) andExpressionBool(f *function, n *cc.AndExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.AndExpressionEq: // EqualityExpression
+		p.equalityExpression(f, n.EqualityExpression, t, mode, flags)
+	case cc.AndExpressionAnd: // AndExpression '&' EqualityExpression
+		p.binaryAndExpression(f, n, t, mode, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) andExpressionAddrof(f *function, n *cc.AndExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.AndExpressionEq: // EqualityExpression
+		p.equalityExpression(f, n.EqualityExpression, t, mode, flags)
+	case cc.AndExpressionAnd: // AndExpression '&' EqualityExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) andExpressionVoid(f *function, n *cc.AndExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.AndExpressionEq: // EqualityExpression
+		p.equalityExpression(f, n.EqualityExpression, t, mode, flags)
+	case cc.AndExpressionAnd: // AndExpression '&' EqualityExpression
+		p.w("_ = ")
+		p.andExpression(f, n, n.Operand.Type(), exprValue, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) andExpressionValue(f *function, n *cc.AndExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.AndExpressionEq: // EqualityExpression
+		p.equalityExpression(f, n.EqualityExpression, t, mode, flags)
+	case cc.AndExpressionAnd: // AndExpression '&' EqualityExpression
+		p.binaryAndExpression(f, n, t, mode, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) binaryAndExpression(f *function, n *cc.AndExpression, t cc.Type, mode exprMode, flags flags) {
+	// AndExpression '&' EqualityExpression
+	switch mode {
+	case exprValue:
+		p.binaryAndExpressionValue(f, n, t, mode, flags)
+	case exprBool:
+		p.binaryAndExpressionBool(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), mode))
+	}
+}
+
+func (p *project) binaryAndExpressionBool(f *function, n *cc.AndExpression, t cc.Type, mode exprMode, flags flags) {
+	defer p.w("%s", p.artithmeticBinaryExpression(n, n.Operand, n.Operand.Type(), &mode, flags))
+	switch {
+	case andOverflows(n.AndExpression.Operand, n.EqualityExpression.Operand, n.Promote()):
+		p.andExpression(f, n.AndExpression, n.Promote(), exprValue, flags)
+		p.w(" &%s", tidyComment(" ", &n.Token))
+		p.equalityExpression(f, n.EqualityExpression, n.Promote(), exprValue, flags|fForceRuntimeConv)
+	default:
+		p.andExpression(f, n.AndExpression, n.Promote(), exprValue, flags)
+		p.w(" &%s", tidyComment(" ", &n.Token))
+		p.equalityExpression(f, n.EqualityExpression, n.Promote(), exprValue, flags)
+	}
+}
+
+func (p *project) binaryAndExpressionValue(f *function, n *cc.AndExpression, t cc.Type, mode exprMode, flags flags) {
+	// AndExpression '&' EqualityExpression
+
+	lt := n.AndExpression.Operand.Type()
+	rt := n.EqualityExpression.Operand.Type()
+	switch lk, rk := lt.Kind(), rt.Kind(); {
+	case
+		lk == cc.UInt128 || rk == cc.UInt128,
+		lk == cc.Int128 || rk == cc.Int128:
+
+		p.binaryAndExpressionUint128(f, n, t, mode, flags)
+		return
+	}
+
+	defer p.w("%s", p.artithmeticBinaryExpression(n, n.Operand, t, &mode, flags))
+	switch {
+	case andOverflows(n.AndExpression.Operand, n.EqualityExpression.Operand, n.Promote()):
+		p.andExpression(f, n.AndExpression, n.Promote(), exprValue, flags)
+		p.w(" &%s", tidyComment(" ", &n.Token))
+		p.equalityExpression(f, n.EqualityExpression, n.Promote(), exprValue, flags|fForceRuntimeConv)
+	default:
+		p.andExpression(f, n.AndExpression, n.Promote(), exprValue, flags)
+		p.w(" &%s", tidyComment(" ", &n.Token))
+		p.equalityExpression(f, n.EqualityExpression, n.Promote(), exprValue, flags)
+	}
+}
+
+func (p *project) binaryAndExpressionUint128(f *function, n *cc.AndExpression, t cc.Type, mode exprMode, flags flags) {
+	// AndExpression '&' EqualityExpression
+	defer p.w("%s", p.artithmeticBinaryExpression(n, n.Operand, t, &mode, flags))
+	p.andExpression(f, n.AndExpression, n.Promote(), exprValue, flags)
+	p.w(".And(")
+	p.equalityExpression(f, n.EqualityExpression, n.Promote(), exprValue, flags)
+	p.w(")")
+}
+
+func andOverflows(lo, ro cc.Operand, promote cc.Type) bool {
+	a, b, ok := getIntOperands(lo, ro)
+	if !ok {
+		return false
+	}
+
+	return overflows(a.And(a, b), promote)
+}
+
+func (p *project) equalityExpression(f *function, n *cc.EqualityExpression, t cc.Type, mode exprMode, flags flags) {
+	switch mode {
+	case exprValue:
+		p.equalityExpressionValue(f, n, t, mode, flags)
+	case exprVoid:
+		p.equalityExpressionVoid(f, n, t, mode, flags)
+	case exprAddrOf:
+		p.equalityExpressionAddrOf(f, n, t, mode, flags)
+	case exprBool:
+		p.equalityExpressionBool(f, n, t, mode, flags)
+	case exprLValue:
+		p.equalityExpressionLValue(f, n, t, mode, flags)
+	case exprPSelect:
+		p.equalityExpressionPSelect(f, n, t, mode, flags)
+	case exprFunc:
+		p.equalityExpressionFunc(f, n, t, mode, flags)
+	case exprSelect:
+		p.equalityExpressionSelect(f, n, t, mode, flags)
+	case exprDecay:
+		p.equalityExpressionDecay(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), mode))
+	}
+}
+
+func (p *project) equalityExpressionDecay(f *function, n *cc.EqualityExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.EqualityExpressionRel: // RelationalExpression
+		p.relationalExpression(f, n.RelationalExpression, t, mode, flags)
+	case cc.EqualityExpressionEq: // EqualityExpression "==" RelationalExpression
+		panic(todo("", p.pos(n)))
+	case cc.EqualityExpressionNeq: // EqualityExpression "!=" RelationalExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) equalityExpressionSelect(f *function, n *cc.EqualityExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.EqualityExpressionRel: // RelationalExpression
+		p.relationalExpression(f, n.RelationalExpression, t, mode, flags)
+	case cc.EqualityExpressionEq: // EqualityExpression "==" RelationalExpression
+		panic(todo("", p.pos(n)))
+	case cc.EqualityExpressionNeq: // EqualityExpression "!=" RelationalExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) equalityExpressionFunc(f *function, n *cc.EqualityExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.EqualityExpressionRel: // RelationalExpression
+		p.relationalExpression(f, n.RelationalExpression, t, mode, flags)
+	case cc.EqualityExpressionEq: // EqualityExpression "==" RelationalExpression
+		panic(todo("", p.pos(n)))
+	case cc.EqualityExpressionNeq: // EqualityExpression "!=" RelationalExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) equalityExpressionPSelect(f *function, n *cc.EqualityExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.EqualityExpressionRel: // RelationalExpression
+		p.relationalExpression(f, n.RelationalExpression, t, mode, flags)
+	case cc.EqualityExpressionEq: // EqualityExpression "==" RelationalExpression
+		panic(todo("", p.pos(n)))
+	case cc.EqualityExpressionNeq: // EqualityExpression "!=" RelationalExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) equalityExpressionLValue(f *function, n *cc.EqualityExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.EqualityExpressionRel: // RelationalExpression
+		p.relationalExpression(f, n.RelationalExpression, t, mode, flags)
+	case cc.EqualityExpressionEq: // EqualityExpression "==" RelationalExpression
+		panic(todo("", p.pos(n)))
+	case cc.EqualityExpressionNeq: // EqualityExpression "!=" RelationalExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) equalityExpressionBool(f *function, n *cc.EqualityExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.EqualityExpressionRel: // RelationalExpression
+		p.relationalExpression(f, n.RelationalExpression, t, mode, flags)
+	case cc.EqualityExpressionEq: // EqualityExpression "==" RelationalExpression
+		p.binaryEqualityExpression(f, n, " == ", t, mode, flags)
+	case cc.EqualityExpressionNeq: // EqualityExpression "!=" RelationalExpression
+		p.binaryEqualityExpression(f, n, " != ", t, mode, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) equalityExpressionAddrOf(f *function, n *cc.EqualityExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.EqualityExpressionRel: // RelationalExpression
+		p.relationalExpression(f, n.RelationalExpression, t, mode, flags)
+	case cc.EqualityExpressionEq: // EqualityExpression "==" RelationalExpression
+		panic(todo("", p.pos(n)))
+	case cc.EqualityExpressionNeq: // EqualityExpression "!=" RelationalExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) equalityExpressionVoid(f *function, n *cc.EqualityExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.EqualityExpressionRel: // RelationalExpression
+		p.relationalExpression(f, n.RelationalExpression, t, mode, flags)
+	default:
+		// case cc.EqualityExpressionEq: // EqualityExpression "==" RelationalExpression
+		// case cc.EqualityExpressionNeq: // EqualityExpression "!=" RelationalExpression
+		p.w("_ = ")
+		p.equalityExpression(f, n, n.Operand.Type(), exprValue, flags)
+	}
+}
+
+func (p *project) equalityExpressionValue(f *function, n *cc.EqualityExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.EqualityExpressionRel: // RelationalExpression
+		p.relationalExpression(f, n.RelationalExpression, t, mode, flags)
+	case cc.EqualityExpressionEq: // EqualityExpression "==" RelationalExpression
+		p.binaryEqualityExpression(f, n, " == ", t, mode, flags)
+	case cc.EqualityExpressionNeq: // EqualityExpression "!=" RelationalExpression
+		p.binaryEqualityExpression(f, n, " != ", t, mode, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) binaryEqualityExpression(f *function, n *cc.EqualityExpression, oper string, t cc.Type, mode exprMode, flags flags) {
+	switch mode {
+	case exprValue:
+		p.binaryEqualityExpressionValue(f, n, oper, t, mode, flags)
+	case exprBool:
+		p.binaryEqualityExpressionBool(f, n, oper, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), mode))
+	}
+}
+
+func (p *project) binaryEqualityExpressionBool(f *function, n *cc.EqualityExpression, oper string, t cc.Type, mode exprMode, flags flags) {
+	defer p.w("%s", p.booleanBinaryExpression(n, n.Operand, t, &mode, flags))
+	p.equalityExpression(f, n.EqualityExpression, n.Promote(), exprValue, flags)
+	p.w(" %s%s", oper, tidyComment(" ", &n.Token))
+	p.relationalExpression(f, n.RelationalExpression, n.Promote(), exprValue, flags)
+}
+
+func (p *project) binaryEqualityExpressionValue(f *function, n *cc.EqualityExpression, oper string, t cc.Type, mode exprMode, flags flags) {
+	defer p.w("%s", p.booleanBinaryExpression(n, n.Operand, t, &mode, flags))
+	p.equalityExpression(f, n.EqualityExpression, n.Promote(), exprValue, flags)
+	p.w(" %s%s", oper, tidyComment(" ", &n.Token))
+	p.relationalExpression(f, n.RelationalExpression, n.Promote(), exprValue, flags)
+}
+
+func (p *project) relationalExpression(f *function, n *cc.RelationalExpression, t cc.Type, mode exprMode, flags flags) {
+	switch mode {
+	case exprValue:
+		p.relationalExpressionValue(f, n, t, mode, flags)
+	case exprVoid:
+		p.relationalExpressionVoid(f, n, t, mode, flags)
+	case exprAddrOf:
+		p.relationalExpressionAddrOf(f, n, t, mode, flags)
+	case exprBool:
+		p.relationalExpressionBool(f, n, t, mode, flags)
+	case exprLValue:
+		p.relationalExpressionLValue(f, n, t, mode, flags)
+	case exprPSelect:
+		p.relationalExpressionPSelect(f, n, t, mode, flags)
+	case exprFunc:
+		p.relationalExpressionFunc(f, n, t, mode, flags)
+	case exprSelect:
+		p.relationalExpressionSelect(f, n, t, mode, flags)
+	case exprDecay:
+		p.relationalExpressionDecay(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), mode))
+	}
+}
+
+func (p *project) relationalExpressionDecay(f *function, n *cc.RelationalExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.RelationalExpressionShift: // ShiftExpression
+		p.shiftExpression(f, n.ShiftExpression, t, mode, flags)
+	case cc.RelationalExpressionLt: // RelationalExpression '<' ShiftExpression
+		panic(todo("", p.pos(n)))
+	case cc.RelationalExpressionGt: // RelationalExpression '>' ShiftExpression
+		panic(todo("", p.pos(n)))
+	case cc.RelationalExpressionLeq: // RelationalExpression "<=" ShiftExpression
+		panic(todo("", p.pos(n)))
+	case cc.RelationalExpressionGeq: // RelationalExpression ">=" ShiftExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) relationalExpressionSelect(f *function, n *cc.RelationalExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.RelationalExpressionShift: // ShiftExpression
+		p.shiftExpression(f, n.ShiftExpression, t, mode, flags)
+	case cc.RelationalExpressionLt: // RelationalExpression '<' ShiftExpression
+		panic(todo("", p.pos(n)))
+	case cc.RelationalExpressionGt: // RelationalExpression '>' ShiftExpression
+		panic(todo("", p.pos(n)))
+	case cc.RelationalExpressionLeq: // RelationalExpression "<=" ShiftExpression
+		panic(todo("", p.pos(n)))
+	case cc.RelationalExpressionGeq: // RelationalExpression ">=" ShiftExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) relationalExpressionFunc(f *function, n *cc.RelationalExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.RelationalExpressionShift: // ShiftExpression
+		p.shiftExpression(f, n.ShiftExpression, t, mode, flags)
+	case cc.RelationalExpressionLt: // RelationalExpression '<' ShiftExpression
+		panic(todo("", p.pos(n)))
+	case cc.RelationalExpressionGt: // RelationalExpression '>' ShiftExpression
+		panic(todo("", p.pos(n)))
+	case cc.RelationalExpressionLeq: // RelationalExpression "<=" ShiftExpression
+		panic(todo("", p.pos(n)))
+	case cc.RelationalExpressionGeq: // RelationalExpression ">=" ShiftExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) relationalExpressionPSelect(f *function, n *cc.RelationalExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.RelationalExpressionShift: // ShiftExpression
+		p.shiftExpression(f, n.ShiftExpression, t, mode, flags)
+	case cc.RelationalExpressionLt: // RelationalExpression '<' ShiftExpression
+		panic(todo("", p.pos(n)))
+	case cc.RelationalExpressionGt: // RelationalExpression '>' ShiftExpression
+		panic(todo("", p.pos(n)))
+	case cc.RelationalExpressionLeq: // RelationalExpression "<=" ShiftExpression
+		panic(todo("", p.pos(n)))
+	case cc.RelationalExpressionGeq: // RelationalExpression ">=" ShiftExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) relationalExpressionLValue(f *function, n *cc.RelationalExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.RelationalExpressionShift: // ShiftExpression
+		p.shiftExpression(f, n.ShiftExpression, t, mode, flags)
+	case cc.RelationalExpressionLt: // RelationalExpression '<' ShiftExpression
+		panic(todo("", p.pos(n)))
+	case cc.RelationalExpressionGt: // RelationalExpression '>' ShiftExpression
+		panic(todo("", p.pos(n)))
+	case cc.RelationalExpressionLeq: // RelationalExpression "<=" ShiftExpression
+		panic(todo("", p.pos(n)))
+	case cc.RelationalExpressionGeq: // RelationalExpression ">=" ShiftExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) relationalExpressionBool(f *function, n *cc.RelationalExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.RelationalExpressionShift: // ShiftExpression
+		p.shiftExpression(f, n.ShiftExpression, t, mode, flags)
+	case cc.RelationalExpressionLt: // RelationalExpression '<' ShiftExpression
+		p.binaryRelationalExpression(f, n, " < ", t, mode, flags)
+	case cc.RelationalExpressionGt: // RelationalExpression '>' ShiftExpression
+		p.binaryRelationalExpression(f, n, " > ", t, mode, flags)
+	case cc.RelationalExpressionLeq: // RelationalExpression "<=" ShiftExpression
+		p.binaryRelationalExpression(f, n, " <= ", t, mode, flags)
+	case cc.RelationalExpressionGeq: // RelationalExpression ">=" ShiftExpression
+		p.binaryRelationalExpression(f, n, " >= ", t, mode, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) relationalExpressionAddrOf(f *function, n *cc.RelationalExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.RelationalExpressionShift: // ShiftExpression
+		p.shiftExpression(f, n.ShiftExpression, t, mode, flags)
+	case cc.RelationalExpressionLt: // RelationalExpression '<' ShiftExpression
+		panic(todo("", p.pos(n)))
+	case cc.RelationalExpressionGt: // RelationalExpression '>' ShiftExpression
+		panic(todo("", p.pos(n)))
+	case cc.RelationalExpressionLeq: // RelationalExpression "<=" ShiftExpression
+		panic(todo("", p.pos(n)))
+	case cc.RelationalExpressionGeq: // RelationalExpression ">=" ShiftExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) relationalExpressionVoid(f *function, n *cc.RelationalExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.RelationalExpressionShift: // ShiftExpression
+		p.shiftExpression(f, n.ShiftExpression, t, mode, flags)
+	default:
+		// case cc.RelationalExpressionLt: // RelationalExpression '<' ShiftExpression
+		// case cc.RelationalExpressionGt: // RelationalExpression '>' ShiftExpression
+		// case cc.RelationalExpressionLeq: // RelationalExpression "<=" ShiftExpression
+		// case cc.RelationalExpressionGeq: // RelationalExpression ">=" ShiftExpression
+		p.w("_ = ")
+		p.relationalExpression(f, n, n.Operand.Type(), exprValue, flags)
+	}
+}
+
+func (p *project) relationalExpressionValue(f *function, n *cc.RelationalExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.RelationalExpressionShift: // ShiftExpression
+		p.shiftExpression(f, n.ShiftExpression, t, mode, flags)
+	case cc.RelationalExpressionLt: // RelationalExpression '<' ShiftExpression
+		p.binaryRelationalExpression(f, n, " < ", t, mode, flags)
+	case cc.RelationalExpressionGt: // RelationalExpression '>' ShiftExpression
+		p.binaryRelationalExpression(f, n, " > ", t, mode, flags)
+	case cc.RelationalExpressionLeq: // RelationalExpression "<=" ShiftExpression
+		p.binaryRelationalExpression(f, n, " <= ", t, mode, flags)
+	case cc.RelationalExpressionGeq: // RelationalExpression ">=" ShiftExpression
+		p.binaryRelationalExpression(f, n, " >= ", t, mode, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) binaryRelationalExpression(f *function, n *cc.RelationalExpression, oper string, t cc.Type, mode exprMode, flags flags) {
+	// RelationalExpression "<=" ShiftExpression
+	lt := n.RelationalExpression.Operand.Type()
+	rt := n.ShiftExpression.Operand.Type()
+	switch lk, rk := lt.Kind(), rt.Kind(); {
+	case
+		lk == cc.UInt128 || rk == cc.UInt128,
+		lk == cc.Int128 || rk == cc.Int128:
+
+		p.binaryRelationalExpressionInt128(f, n, oper, t, mode, flags)
+		return
+	}
+
+	defer p.w("%s", p.booleanBinaryExpression(n, n.Operand, t, &mode, flags))
+	p.relationalExpression(f, n.RelationalExpression, n.Promote(), exprValue, flags)
+	p.w(" %s%s", oper, tidyComment(" ", &n.Token))
+	p.shiftExpression(f, n.ShiftExpression, n.Promote(), exprValue, flags)
+}
+
+func (p *project) binaryRelationalExpressionInt128(f *function, n *cc.RelationalExpression, oper string, t cc.Type, mode exprMode, flags flags) {
+	// RelationalExpression "<=" ShiftExpression
+	defer p.w("%s", p.booleanBinaryExpression(n, n.Operand, t, &mode, flags))
+	p.relationalExpression(f, n.RelationalExpression, n.Promote(), exprValue, flags)
+	p.w(".Cmp(")
+	p.shiftExpression(f, n.ShiftExpression, n.Promote(), exprValue, flags)
+	p.w(") %s 0", oper)
+}
+
+func (p *project) shiftExpression(f *function, n *cc.ShiftExpression, t cc.Type, mode exprMode, flags flags) {
+	switch mode {
+	case exprValue:
+		p.shiftExpressionValue(f, n, t, mode, flags)
+	case exprVoid:
+		p.shiftExpressionVoid(f, n, t, mode, flags)
+	case exprAddrOf:
+		p.shiftExpressionAddrOf(f, n, t, mode, flags)
+	case exprBool:
+		p.shiftExpressionBool(f, n, t, mode, flags)
+	case exprLValue:
+		p.shiftExpressionLValue(f, n, t, mode, flags)
+	case exprPSelect:
+		p.shiftExpressionPSelect(f, n, t, mode, flags)
+	case exprFunc:
+		p.shiftExpressionFunc(f, n, t, mode, flags)
+	case exprSelect:
+		p.shiftExpressionSelect(f, n, t, mode, flags)
+	case exprDecay:
+		p.shiftExpressionDecay(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), mode))
+	}
+}
+
+func (p *project) shiftExpressionDecay(f *function, n *cc.ShiftExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ShiftExpressionAdd: // AdditiveExpression
+		p.additiveExpression(f, n.AdditiveExpression, t, mode, flags)
+	case cc.ShiftExpressionLsh: // ShiftExpression "<<" AdditiveExpression
+		panic(todo("", p.pos(n)))
+	case cc.ShiftExpressionRsh: // ShiftExpression ">>" AdditiveExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) shiftExpressionSelect(f *function, n *cc.ShiftExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ShiftExpressionAdd: // AdditiveExpression
+		p.additiveExpression(f, n.AdditiveExpression, t, mode, flags)
+	case cc.ShiftExpressionLsh: // ShiftExpression "<<" AdditiveExpression
+		panic(todo("", p.pos(n)))
+	case cc.ShiftExpressionRsh: // ShiftExpression ">>" AdditiveExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) shiftExpressionFunc(f *function, n *cc.ShiftExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ShiftExpressionAdd: // AdditiveExpression
+		p.additiveExpression(f, n.AdditiveExpression, t, mode, flags)
+	case cc.ShiftExpressionLsh: // ShiftExpression "<<" AdditiveExpression
+		panic(todo("", p.pos(n)))
+	case cc.ShiftExpressionRsh: // ShiftExpression ">>" AdditiveExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) shiftExpressionPSelect(f *function, n *cc.ShiftExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ShiftExpressionAdd: // AdditiveExpression
+		p.additiveExpression(f, n.AdditiveExpression, t, mode, flags)
+	case cc.ShiftExpressionLsh: // ShiftExpression "<<" AdditiveExpression
+		panic(todo("", p.pos(n)))
+	case cc.ShiftExpressionRsh: // ShiftExpression ">>" AdditiveExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) shiftExpressionLValue(f *function, n *cc.ShiftExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ShiftExpressionAdd: // AdditiveExpression
+		p.additiveExpression(f, n.AdditiveExpression, t, mode, flags)
+	case cc.ShiftExpressionLsh: // ShiftExpression "<<" AdditiveExpression
+		panic(todo("", p.pos(n)))
+	case cc.ShiftExpressionRsh: // ShiftExpression ">>" AdditiveExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) shiftExpressionBool(f *function, n *cc.ShiftExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ShiftExpressionAdd: // AdditiveExpression
+		p.additiveExpression(f, n.AdditiveExpression, t, mode, flags)
+	case cc.ShiftExpressionLsh: // ShiftExpression "<<" AdditiveExpression
+		p.binaryShiftExpression(f, n, "<<", t, mode, flags)
+	case cc.ShiftExpressionRsh: // ShiftExpression ">>" AdditiveExpression
+		p.binaryShiftExpression(f, n, ">>", t, mode, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) shiftExpressionAddrOf(f *function, n *cc.ShiftExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ShiftExpressionAdd: // AdditiveExpression
+		p.additiveExpression(f, n.AdditiveExpression, t, mode, flags)
+	case cc.ShiftExpressionLsh: // ShiftExpression "<<" AdditiveExpression
+		panic(todo("", p.pos(n)))
+	case cc.ShiftExpressionRsh: // ShiftExpression ">>" AdditiveExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) shiftExpressionVoid(f *function, n *cc.ShiftExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ShiftExpressionAdd: // AdditiveExpression
+		p.additiveExpression(f, n.AdditiveExpression, t, mode, flags)
+	case cc.ShiftExpressionLsh: // ShiftExpression "<<" AdditiveExpression
+		panic(todo("", p.pos(n)))
+	case cc.ShiftExpressionRsh: // ShiftExpression ">>" AdditiveExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) shiftExpressionValue(f *function, n *cc.ShiftExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.ShiftExpressionAdd: // AdditiveExpression
+		p.additiveExpression(f, n.AdditiveExpression, t, mode, flags)
+	case cc.ShiftExpressionLsh: // ShiftExpression "<<" AdditiveExpression
+		p.binaryShiftExpression(f, n, "<<", t, mode, flags)
+	case cc.ShiftExpressionRsh: // ShiftExpression ">>" AdditiveExpression
+		p.binaryShiftExpression(f, n, ">>", t, mode, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) binaryShiftExpression(f *function, n *cc.ShiftExpression, oper string, t cc.Type, mode exprMode, flags flags) {
+	// ShiftExpression "<<" AdditiveExpression
+	switch mode {
+	case exprValue:
+		p.binaryShiftExpressionValue(f, n, oper, t, mode, flags)
+	case exprBool:
+		p.binaryShiftExpressionBool(f, n, oper, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), mode))
+	}
+}
+
+func (p *project) todo(n cc.Node, t cc.Type) {
+	p.w("func() %s { panic(`%v: TODO (%v)`)}()", p.typ(n, t), n.Position(), origin(2))
+}
+
+func (p *project) binaryShiftExpressionBool(f *function, n *cc.ShiftExpression, oper string, t cc.Type, mode exprMode, flags flags) {
+	defer p.w("%s", p.artithmeticBinaryExpression(n, n.Operand, n.Operand.Type(), &mode, flags))
+	switch {
+	case n.ShiftExpression.Operand.Type().IsBitFieldType():
+		p.w("(")
+		p.shiftExpression(f, n.ShiftExpression, n.Operand.Type(), exprValue, flags)
+		p.w(" %s%s", oper, tidyComment(" ", &n.Token))
+		p.additiveExpression(f, n.AdditiveExpression, n.Promote(), exprValue, flags)
+		p.w(")&%#x", bfValueMask(n.ShiftExpression.Operand.Type().BitField()))
+	case shiftOverflows(n, n.ShiftExpression.Operand, n.AdditiveExpression.Operand, oper, n.Operand.Type()):
+		p.shiftExpression(f, n.ShiftExpression, n.Operand.Type(), exprValue, flags|fForceRuntimeConv)
+		p.w(" %s%s", oper, tidyComment(" ", &n.Token))
+		p.additiveExpression(f, n.AdditiveExpression, n.Promote(), exprValue, flags)
+	case isConstInteger(n.ShiftExpression.Operand):
+		s := p.convertNil(n, n.Operand.Type(), 0)
+		p.shiftExpression(f, n.ShiftExpression, n.Operand.Type(), exprValue, flags)
+		p.w("%s %s%s", s, oper, tidyComment(" ", &n.Token))
+		p.additiveExpression(f, n.AdditiveExpression, n.Promote(), exprValue, flags)
+	default:
+		p.shiftExpression(f, n.ShiftExpression, n.Operand.Type(), exprValue, flags)
+		p.w(" %s%s", oper, tidyComment(" ", &n.Token))
+		p.additiveExpression(f, n.AdditiveExpression, n.Promote(), exprValue, flags)
+	}
+}
+
+func shiftOp(s string) string {
+	switch s {
+	case "<<":
+		return "Shl"
+	case ">>":
+		return "Shr"
+	default:
+		panic(todo("%q", s))
+	}
+}
+
+func bfValueMask(bf cc.Field) uint64 {
+	return uint64(1)< mathutil.MaxUint {
+		return true
+	}
+
+	switch oper {
+	case "<<":
+		return overflows(a.Lsh(a, uint(bits)), result)
+	case ">>":
+		return overflows(a.Rsh(a, uint(bits)), result)
+	default:
+		panic(todo("", pos(n)))
+	}
+}
+
+func (p *project) additiveExpression(f *function, n *cc.AdditiveExpression, t cc.Type, mode exprMode, flags flags) {
+	switch mode {
+	case exprValue:
+		p.additiveExpressionValue(f, n, t, mode, flags)
+	case exprVoid:
+		p.additiveExpressionVoid(f, n, t, mode, flags)
+	case exprAddrOf:
+		p.additiveExpressionAddrOf(f, n, t, mode, flags)
+	case exprBool:
+		p.additiveExpressionBool(f, n, t, mode, flags)
+	case exprLValue:
+		p.additiveExpressionLValue(f, n, t, mode, flags)
+	case exprPSelect:
+		p.additiveExpressionPSelect(f, n, t, mode, flags)
+	case exprFunc:
+		p.additiveExpressionFunc(f, n, t, mode, flags)
+	case exprSelect:
+		p.additiveExpressionSelect(f, n, t, mode, flags)
+	case exprDecay:
+		p.additiveExpressionDecay(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), mode))
+	}
+}
+
+func (p *project) additiveExpressionDecay(f *function, n *cc.AdditiveExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.AdditiveExpressionMul: // MultiplicativeExpression
+		p.multiplicativeExpression(f, n.MultiplicativeExpression, t, mode, flags)
+	case cc.AdditiveExpressionAdd: // AdditiveExpression '+' MultiplicativeExpression
+		panic(todo("", p.pos(n)))
+	case cc.AdditiveExpressionSub: // AdditiveExpression '-' MultiplicativeExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) additiveExpressionSelect(f *function, n *cc.AdditiveExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.AdditiveExpressionMul: // MultiplicativeExpression
+		p.multiplicativeExpression(f, n.MultiplicativeExpression, t, mode, flags)
+	case cc.AdditiveExpressionAdd: // AdditiveExpression '+' MultiplicativeExpression
+		panic(todo("", p.pos(n)))
+	case cc.AdditiveExpressionSub: // AdditiveExpression '-' MultiplicativeExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) additiveExpressionFunc(f *function, n *cc.AdditiveExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.AdditiveExpressionMul: // MultiplicativeExpression
+		p.multiplicativeExpression(f, n.MultiplicativeExpression, t, mode, flags)
+	case cc.AdditiveExpressionAdd: // AdditiveExpression '+' MultiplicativeExpression
+		panic(todo("", p.pos(n)))
+	case cc.AdditiveExpressionSub: // AdditiveExpression '-' MultiplicativeExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) additiveExpressionPSelect(f *function, n *cc.AdditiveExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.AdditiveExpressionMul: // MultiplicativeExpression
+		p.multiplicativeExpression(f, n.MultiplicativeExpression, t, mode, flags)
+	case cc.AdditiveExpressionAdd: // AdditiveExpression '+' MultiplicativeExpression
+		p.w("(*%s)(unsafe.Pointer(", p.typ(n, t.Elem()))
+		p.additiveExpression(f, n, t, exprValue, flags)
+		p.w("))")
+	case cc.AdditiveExpressionSub: // AdditiveExpression '-' MultiplicativeExpression
+		p.w("(*%s)(unsafe.Pointer(", p.typ(n, t.Elem()))
+		p.additiveExpression(f, n, t, exprValue, flags)
+
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) additiveExpressionLValue(f *function, n *cc.AdditiveExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.AdditiveExpressionMul: // MultiplicativeExpression
+		p.multiplicativeExpression(f, n.MultiplicativeExpression, t, mode, flags)
+	case cc.AdditiveExpressionAdd: // AdditiveExpression '+' MultiplicativeExpression
+		panic(todo("", p.pos(n)))
+	case cc.AdditiveExpressionSub: // AdditiveExpression '-' MultiplicativeExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) additiveExpressionBool(f *function, n *cc.AdditiveExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.AdditiveExpressionMul: // MultiplicativeExpression
+		p.multiplicativeExpression(f, n.MultiplicativeExpression, t, mode, flags)
+	case cc.AdditiveExpressionAdd: // AdditiveExpression '+' MultiplicativeExpression
+		p.binaryAdditiveExpression(f, n, "+", t, mode, flags)
+	case cc.AdditiveExpressionSub: // AdditiveExpression '-' MultiplicativeExpression
+		p.binaryAdditiveExpression(f, n, "-", t, mode, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) additiveExpressionAddrOf(f *function, n *cc.AdditiveExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.AdditiveExpressionMul: // MultiplicativeExpression
+		p.multiplicativeExpression(f, n.MultiplicativeExpression, t, mode, flags)
+	case cc.AdditiveExpressionAdd: // AdditiveExpression '+' MultiplicativeExpression
+		panic(todo("", p.pos(n)))
+	case cc.AdditiveExpressionSub: // AdditiveExpression '-' MultiplicativeExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) additiveExpressionVoid(f *function, n *cc.AdditiveExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.AdditiveExpressionMul: // MultiplicativeExpression
+		p.multiplicativeExpression(f, n.MultiplicativeExpression, t, mode, flags)
+	case
+		cc.AdditiveExpressionAdd, // AdditiveExpression '+' MultiplicativeExpression
+		cc.AdditiveExpressionSub: // AdditiveExpression '-' MultiplicativeExpression
+
+		p.w("_ = ")
+		p.additiveExpression(f, n, n.Operand.Type(), exprValue, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) additiveExpressionValue(f *function, n *cc.AdditiveExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.AdditiveExpressionMul: // MultiplicativeExpression
+		p.multiplicativeExpression(f, n.MultiplicativeExpression, t, mode, flags)
+	case cc.AdditiveExpressionAdd: // AdditiveExpression '+' MultiplicativeExpression
+		p.binaryAdditiveExpression(f, n, "+", t, mode, flags)
+	case cc.AdditiveExpressionSub: // AdditiveExpression '-' MultiplicativeExpression
+		p.binaryAdditiveExpression(f, n, "-", t, mode, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) binaryAdditiveExpression(f *function, n *cc.AdditiveExpression, oper string, t cc.Type, mode exprMode, flags flags) {
+	// AdditiveExpression '+' MultiplicativeExpression
+	switch mode {
+	case exprValue:
+		p.binaryAdditiveExpressionValue(f, n, oper, t, mode, flags)
+	case exprBool:
+		p.binaryAdditiveExpressionBool(f, n, oper, t, mode, flags)
+	default:
+		panic(todo("", mode))
+	}
+}
+
+func (p *project) binaryAdditiveExpressionBool(f *function, n *cc.AdditiveExpression, oper string, t cc.Type, mode exprMode, flags flags) {
+	// AdditiveExpression '+' MultiplicativeExpression
+	defer p.w("%s", p.artithmeticBinaryExpression(n, n.Operand, n.Operand.Type(), &mode, flags))
+	lo := n.AdditiveExpression.Operand
+	ro := n.MultiplicativeExpression.Operand
+	lt := lo.Type()
+	rt := ro.Type()
+	switch {
+	case lt.Kind() == cc.Ptr && rt.Kind() == cc.Ptr && oper == "-":
+		p.additiveExpression(f, n.AdditiveExpression, n.Promote(), exprValue, flags)
+		p.w(" %s%s", oper, tidyComment(" ", &n.Token))
+		p.multiplicativeExpression(f, n.MultiplicativeExpression, n.Promote(), exprValue, flags)
+	case lt.IsArithmeticType() && rt.IsArithmeticType(): // x +- y
+		defer p.w("%s", p.bitFieldPatch2(n, lo, ro, n.Promote())) //TODO bit field big endian
+		switch {
+		case intAddOverflows(n, lo, ro, oper, n.Promote()): // i +- j
+			p.additiveExpression(f, n.AdditiveExpression, n.Promote(), exprValue, flags)
+			p.w(" %s%s", oper, tidyComment(" ", &n.Token))
+			p.multiplicativeExpression(f, n.MultiplicativeExpression, n.Promote(), exprValue, flags|fForceRuntimeConv)
+		default:
+			var s string
+			if isRealType(n.Operand) && n.Operand.Value() != nil {
+				s = p.convertNil(n, n.Promote(), flags)
+			}
+			p.additiveExpression(f, n.AdditiveExpression, n.Promote(), exprValue, flags)
+			p.w("%s %s%s", s, oper, tidyComment(" ", &n.Token))
+			p.multiplicativeExpression(f, n.MultiplicativeExpression, n.Promote(), exprValue, flags)
+		}
+	default:
+		panic(todo("", n.Position(), lt, rt, oper))
+	}
+}
+
+func (p *project) binaryAdditiveExpressionValue(f *function, n *cc.AdditiveExpression, oper string, t cc.Type, mode exprMode, flags flags) {
+	// AdditiveExpression '+' MultiplicativeExpression
+
+	lt := n.AdditiveExpression.Operand.Type()
+	rt := n.MultiplicativeExpression.Operand.Type()
+	switch lk, rk := lt.Kind(), rt.Kind(); {
+	case
+		lk == cc.UInt128 || rk == cc.UInt128,
+		lk == cc.Int128 || rk == cc.Int128:
+
+		p.binaryAdditiveExpressionUint128(f, n, oper, t, mode, flags)
+		return
+	}
+
+	defer p.w("%s", p.artithmeticBinaryExpression(n, n.Operand, t, &mode, flags))
+	lo := n.AdditiveExpression.Operand
+	ro := n.MultiplicativeExpression.Operand
+	switch {
+	case lt.IsArithmeticType() && rt.IsArithmeticType(): // x +- y
+		defer p.w("%s", p.bitFieldPatch2(n, lo, ro, n.Promote())) //TODO bit field big endian
+		switch {
+		case intAddOverflows(n, lo, ro, oper, n.Promote()): // i +- j
+			p.additiveExpression(f, n.AdditiveExpression, n.Promote(), exprValue, flags)
+			p.w(" %s%s", oper, tidyComment(" ", &n.Token))
+			p.multiplicativeExpression(f, n.MultiplicativeExpression, n.Promote(), exprValue, flags|fForceRuntimeConv)
+		default:
+			var s string
+			if isRealType(n.Operand) && n.Operand.Value() != nil {
+				s = p.convertNil(n, n.Promote(), flags)
+			}
+			p.additiveExpression(f, n.AdditiveExpression, n.Promote(), exprValue, flags)
+			p.w("%s %s%s", s, oper, tidyComment(" ", &n.Token))
+			p.multiplicativeExpression(f, n.MultiplicativeExpression, n.Promote(), exprValue, flags)
+		}
+	case lt.Kind() == cc.Ptr && rt.IsIntegerType(): // p +- i
+		p.additiveExpression(f, n.AdditiveExpression, lt, exprValue, flags)
+		p.w(" %s%s uintptr(", oper, tidyComment(" ", &n.Token))
+		p.multiplicativeExpression(f, n.MultiplicativeExpression, rt, exprValue, flags)
+		p.w(")")
+		if sz := lt.Elem().Size(); sz != 1 {
+			p.w("*%d", sz)
+		}
+	case lt.Kind() == cc.Array && rt.IsIntegerType(): // p +- i
+		p.additiveExpression(f, n.AdditiveExpression, lt, exprDecay, flags)
+		p.w(" %s%s uintptr(", oper, tidyComment(" ", &n.Token))
+		p.multiplicativeExpression(f, n.MultiplicativeExpression, rt, exprValue, flags)
+		p.w(")")
+		if sz := lt.Elem().Size(); sz != 1 {
+			p.w("*%d", sz)
+		}
+	case lt.IsIntegerType() && rt.Kind() == cc.Ptr: // i +- p
+		p.w("uintptr(")
+		p.additiveExpression(f, n.AdditiveExpression, lt, exprValue, flags)
+		p.w(")")
+		if sz := rt.Elem().Size(); sz != 1 {
+			p.w("*%d", sz)
+		}
+		p.w(" %s%s ", oper, tidyComment(" ", &n.Token))
+		p.multiplicativeExpression(f, n.MultiplicativeExpression, rt, exprValue, flags)
+	case lt.IsIntegerType() && rt.Kind() == cc.Array: // i +- p
+		panic(todo("", p.pos(n)))
+	case lt.Kind() == cc.Ptr && rt.Kind() == cc.Ptr && oper == "-": // p - q
+		p.w("(")
+		p.additiveExpression(f, n.AdditiveExpression, n.Operand.Type(), exprValue, flags)
+		p.w(" %s%s", oper, tidyComment(" ", &n.Token))
+		p.multiplicativeExpression(f, n.MultiplicativeExpression, n.Operand.Type(), exprValue, flags)
+		p.w(")/%d", lt.Elem().Size())
+	case lt.Kind() == cc.Ptr && rt.Kind() == cc.Array && oper == "-": // p - q
+		defer p.w("%s", p.convertType(n, nil, n.Operand.Type(), 0))
+		p.w("(")
+		p.additiveExpression(f, n.AdditiveExpression, lt, exprValue, flags)
+		p.w(" %s%s", oper, tidyComment(" ", &n.Token))
+		p.multiplicativeExpression(f, n.MultiplicativeExpression, rt.Decay(), exprDecay, flags)
+		p.w(")/%d", lt.Elem().Size())
+	case lt.Kind() == cc.Array && rt.Kind() == cc.Ptr && oper == "-": // p - q
+		panic(todo("", p.pos(n)))
+	case lt.Kind() == cc.Array && rt.Kind() == cc.Array && oper == "-": // p - q
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("", n.Position(), lt, rt, oper))
+	}
+}
+
+func (p *project) binaryAdditiveExpressionUint128(f *function, n *cc.AdditiveExpression, oper string, t cc.Type, mode exprMode, flags flags) {
+	// AdditiveExpression '+' MultiplicativeExpression
+	defer p.w("%s", p.artithmeticBinaryExpression(n, n.Operand, t, &mode, flags))
+	p.additiveExpression(f, n.AdditiveExpression, n.Promote(), exprValue, flags)
+	switch oper {
+	case "+":
+		p.w(".Add(")
+	case "-":
+		p.w(".Sub(")
+	default:
+		panic(todo("%q", oper))
+	}
+	p.multiplicativeExpression(f, n.MultiplicativeExpression, n.Promote(), exprValue, flags)
+	p.w(")")
+}
+
+func (p *project) bitFieldPatch2(n cc.Node, a, b cc.Operand, promote cc.Type) string {
+	//TODO bit field big endian
+	var m uint64
+	var w int
+	switch {
+	case a.Type().IsBitFieldType():
+		bf := a.Type().BitField()
+		w = bf.BitFieldWidth()
+		m = bf.Mask() >> bf.BitFieldOffset()
+		if b.Type().IsBitFieldType() {
+			bf = b.Type().BitField()
+			w2 := bf.BitFieldWidth()
+			if w2 != w {
+				panic(todo("", p.pos(n)))
+			}
+		}
+	case b.Type().IsBitFieldType():
+		bf := b.Type().BitField()
+		w = bf.BitFieldWidth()
+		m = bf.Mask() >> bf.BitFieldOffset()
+	default:
+		return ""
+	}
+
+	p.w("((")
+	switch {
+	case promote.IsSignedType():
+		n := int(promote.Size())*8 - w
+		var s string
+		switch promote.Size() {
+		case 4:
+			s = fmt.Sprintf(")&%#x", int32(m))
+		default:
+			s = fmt.Sprintf(")&%#x", m)
+		}
+		if n != 0 {
+			s += fmt.Sprintf("<<%d>>%[1]d", n)
+		}
+		return ")" + s
+	default:
+		return fmt.Sprintf(")&%#x)", m)
+	}
+}
+
+func intAddOverflows(n cc.Node, lo, ro cc.Operand, oper string, promote cc.Type) bool {
+	a, b, ok := getIntOperands(lo, ro)
+	if !ok {
+		return false
+	}
+
+	switch oper {
+	case "+":
+		return overflows(a.Add(a, b), promote)
+	case "-":
+		return overflows(a.Sub(a, b), promote)
+	default:
+		panic(todo("", pos(n)))
+	}
+}
+
+func getIntOperands(a, b cc.Operand) (x, y *big.Int, ok bool) {
+	switch n := a.Value().(type) {
+	case cc.Int64Value:
+		x = big.NewInt(int64(n))
+	case cc.Uint64Value:
+		x = big.NewInt(0).SetUint64(uint64(n))
+	default:
+		return nil, nil, false
+	}
+
+	switch n := b.Value().(type) {
+	case cc.Int64Value:
+		return x, big.NewInt(int64(n)), true
+	case cc.Uint64Value:
+		return x, big.NewInt(0).SetUint64(uint64(n)), true
+	default:
+		return nil, nil, false
+	}
+}
+
+func overflows(n *big.Int, promote cc.Type) bool {
+	switch k := promote.Kind(); {
+	case k == cc.Int128, k == cc.UInt128:
+		return false
+	case isSigned(promote):
+		switch promote.Size() {
+		case 4:
+			return n.Cmp(minInt32) < 0 || n.Cmp(maxInt32) > 0
+		case 8:
+			return n.Cmp(minInt64) < 0 || n.Cmp(maxInt64) > 0
+		}
+	default:
+		switch promote.Size() {
+		case 4:
+			return n.Sign() < 0 || n.Cmp(maxUint32) > 0
+		case 8:
+			return n.Sign() < 0 || n.Cmp(maxUint64) > 0
+		}
+	}
+	panic(todo("", promote.Size(), promote))
+}
+
+func (p *project) multiplicativeExpression(f *function, n *cc.MultiplicativeExpression, t cc.Type, mode exprMode, flags flags) {
+	switch mode {
+	case exprValue:
+		p.multiplicativeExpressionValue(f, n, t, mode, flags)
+	case exprVoid:
+		p.multiplicativeExpressionVoid(f, n, t, mode, flags)
+	case exprAddrOf:
+		p.multiplicativeExpressionAddrOf(f, n, t, mode, flags)
+	case exprBool:
+		p.multiplicativeExpressionBool(f, n, t, mode, flags)
+	case exprLValue:
+		p.multiplicativeExpressionLValue(f, n, t, mode, flags)
+	case exprPSelect:
+		p.multiplicativeExpressionPSelect(f, n, t, mode, flags)
+	case exprFunc:
+		p.multiplicativeExpressionFunc(f, n, t, mode, flags)
+	case exprSelect:
+		p.multiplicativeExpressionSelect(f, n, t, mode, flags)
+	case exprDecay:
+		p.multiplicativeExpressionDecay(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), mode))
+	}
+}
+
+func (p *project) multiplicativeExpressionDecay(f *function, n *cc.MultiplicativeExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.MultiplicativeExpressionCast: // CastExpression
+		p.castExpression(f, n.CastExpression, t, mode, flags)
+	case cc.MultiplicativeExpressionMul: // MultiplicativeExpression '*' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.MultiplicativeExpressionDiv: // MultiplicativeExpression '/' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.MultiplicativeExpressionMod: // MultiplicativeExpression '%' CastExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) multiplicativeExpressionSelect(f *function, n *cc.MultiplicativeExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.MultiplicativeExpressionCast: // CastExpression
+		p.castExpression(f, n.CastExpression, t, mode, flags)
+	case cc.MultiplicativeExpressionMul: // MultiplicativeExpression '*' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.MultiplicativeExpressionDiv: // MultiplicativeExpression '/' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.MultiplicativeExpressionMod: // MultiplicativeExpression '%' CastExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) multiplicativeExpressionFunc(f *function, n *cc.MultiplicativeExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.MultiplicativeExpressionCast: // CastExpression
+		p.castExpression(f, n.CastExpression, t, mode, flags)
+	case cc.MultiplicativeExpressionMul: // MultiplicativeExpression '*' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.MultiplicativeExpressionDiv: // MultiplicativeExpression '/' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.MultiplicativeExpressionMod: // MultiplicativeExpression '%' CastExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) multiplicativeExpressionPSelect(f *function, n *cc.MultiplicativeExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.MultiplicativeExpressionCast: // CastExpression
+		p.castExpression(f, n.CastExpression, t, mode, flags)
+	case cc.MultiplicativeExpressionMul: // MultiplicativeExpression '*' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.MultiplicativeExpressionDiv: // MultiplicativeExpression '/' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.MultiplicativeExpressionMod: // MultiplicativeExpression '%' CastExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) multiplicativeExpressionLValue(f *function, n *cc.MultiplicativeExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.MultiplicativeExpressionCast: // CastExpression
+		p.castExpression(f, n.CastExpression, t, mode, flags)
+	case cc.MultiplicativeExpressionMul: // MultiplicativeExpression '*' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.MultiplicativeExpressionDiv: // MultiplicativeExpression '/' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.MultiplicativeExpressionMod: // MultiplicativeExpression '%' CastExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) multiplicativeExpressionBool(f *function, n *cc.MultiplicativeExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.MultiplicativeExpressionCast: // CastExpression
+		p.castExpression(f, n.CastExpression, t, mode, flags)
+	case
+		cc.MultiplicativeExpressionMul, // MultiplicativeExpression '*' CastExpression
+		cc.MultiplicativeExpressionDiv, // MultiplicativeExpression '/' CastExpression
+		cc.MultiplicativeExpressionMod: // MultiplicativeExpression '%' CastExpression
+
+		p.w("(")
+		defer p.w(")")
+		defer p.w(" != 0 ")
+		p.multiplicativeExpression(f, n, t, exprValue, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) multiplicativeExpressionAddrOf(f *function, n *cc.MultiplicativeExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.MultiplicativeExpressionCast: // CastExpression
+		p.castExpression(f, n.CastExpression, t, mode, flags)
+	case cc.MultiplicativeExpressionMul: // MultiplicativeExpression '*' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.MultiplicativeExpressionDiv: // MultiplicativeExpression '/' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.MultiplicativeExpressionMod: // MultiplicativeExpression '%' CastExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) multiplicativeExpressionVoid(f *function, n *cc.MultiplicativeExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.MultiplicativeExpressionCast: // CastExpression
+		p.castExpression(f, n.CastExpression, t, mode, flags)
+	case cc.MultiplicativeExpressionMul: // MultiplicativeExpression '*' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.MultiplicativeExpressionDiv: // MultiplicativeExpression '/' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.MultiplicativeExpressionMod: // MultiplicativeExpression '%' CastExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) multiplicativeExpressionValue(f *function, n *cc.MultiplicativeExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.MultiplicativeExpressionCast: // CastExpression
+		p.castExpression(f, n.CastExpression, t, mode, flags)
+	case cc.MultiplicativeExpressionMul: // MultiplicativeExpression '*' CastExpression
+		p.binaryMultiplicativeExpression(f, n, "*", t, mode, flags)
+	case cc.MultiplicativeExpressionDiv: // MultiplicativeExpression '/' CastExpression
+		p.binaryMultiplicativeExpression(f, n, "/", t, mode, flags)
+	case cc.MultiplicativeExpressionMod: // MultiplicativeExpression '%' CastExpression
+		p.binaryMultiplicativeExpression(f, n, "%", t, mode, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) binaryMultiplicativeExpression(f *function, n *cc.MultiplicativeExpression, oper string, t cc.Type, mode exprMode, flags flags) {
+	// MultiplicativeExpression '*' CastExpression
+	switch mode {
+	case exprValue:
+		p.binaryMultiplicativeExpressionValue(f, n, oper, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), mode))
+	}
+}
+
+func (p *project) binaryMultiplicativeExpressionValue(f *function, n *cc.MultiplicativeExpression, oper string, t cc.Type, mode exprMode, flags flags) {
+	// MultiplicativeExpression '*' CastExpression
+
+	lt := n.MultiplicativeExpression.Operand.Type()
+	rt := n.CastExpression.Operand.Type()
+	switch lk, rk := lt.Kind(), rt.Kind(); {
+	case
+		lk == cc.UInt128 || rk == cc.UInt128,
+		lk == cc.Int128 || rk == cc.Int128:
+
+		p.binaryMultiplicativeExpressionUint128(f, n, oper, t, mode, flags)
+		return
+	}
+
+	defer p.w("%s", p.artithmeticBinaryExpression(n, n.Operand, t, &mode, flags))
+	switch {
+	case intMulOverflows(n, n.Operand, n.MultiplicativeExpression.Operand, n.CastExpression.Operand, oper, n.Promote()):
+		p.multiplicativeExpression(f, n.MultiplicativeExpression, n.Promote(), exprValue, flags|fForceRuntimeConv)
+		p.w(" %s%s", oper, tidyComment(" ", &n.Token))
+		p.castExpression(f, n.CastExpression, n.Promote(), exprValue, flags|fForceRuntimeConv)
+	default:
+		defer p.w("%s", p.bitFieldPatch2(n, n.MultiplicativeExpression.Operand, n.CastExpression.Operand, n.Promote())) //TODO bit field big endian
+		var s string
+		if isRealType(n.Operand) && n.Operand.Value() != nil {
+			s = p.convertNil(n, n.Promote(), flags)
+		}
+		p.multiplicativeExpression(f, n.MultiplicativeExpression, n.Promote(), exprValue, flags)
+		p.w("%s %s%s", s, oper, tidyComment(" ", &n.Token))
+		if (oper == "/" || oper == "%") && (isZeroReal(n.MultiplicativeExpression.Operand) || isZeroReal(n.CastExpression.Operand)) {
+			p.w("%s%sFrom%[2]s(", p.task.crt, p.helperType(n, n.Promote()))
+			defer p.w(")")
+		}
+		p.castExpression(f, n.CastExpression, n.Promote(), exprValue, flags)
+	}
+}
+
+func (p *project) binaryMultiplicativeExpressionUint128(f *function, n *cc.MultiplicativeExpression, oper string, t cc.Type, mode exprMode, flags flags) {
+	// MultiplicativeExpression '*' CastExpression
+	defer p.w("%s", p.artithmeticBinaryExpression(n, n.Operand, t, &mode, flags))
+	p.multiplicativeExpression(f, n.MultiplicativeExpression, n.Promote(), exprValue, flags)
+	switch oper {
+	case "*":
+		p.w(".Mul(")
+	case "/":
+		p.w(".Div(")
+	case "%":
+		p.w(".Mod(")
+	default:
+		panic(todo("%q", oper))
+	}
+	p.castExpression(f, n.CastExpression, n.Promote(), exprValue, flags)
+	p.w(")")
+}
+
+func isZeroReal(op cc.Operand) bool {
+	switch x := op.Value().(type) {
+	case cc.Float32Value:
+		return x == 0
+	case cc.Float64Value:
+		return x == 0
+	default:
+		return false
+	}
+}
+
+func intMulOverflows(n cc.Node, r, lo, ro cc.Operand, oper string, promote cc.Type) bool {
+	if (isReal(lo) && !isInf(lo) || isReal(ro) && !isInf(ro)) && isInf(r) {
+		return true
+	}
+
+	a, b, ok := getIntOperands(lo, ro)
+	if !ok {
+		return false
+	}
+
+	switch oper {
+	case "*":
+		return overflows(a.Mul(a, b), promote)
+	case "/":
+		if b.Sign() == 0 {
+			return true
+		}
+
+		return overflows(a.Div(a, b), promote)
+	case "%":
+		if b.Sign() == 0 {
+			return true
+		}
+
+		return overflows(a.Mod(a, b), promote)
+	default:
+		panic(todo("", pos(n)))
+	}
+}
+
+func isReal(op cc.Operand) bool {
+	switch op.Value().(type) {
+	case cc.Float32Value, cc.Float64Value:
+		return true
+	default:
+		return false
+	}
+}
+
+func isInf(op cc.Operand) bool {
+	switch x := op.Value().(type) {
+	case cc.Float32Value:
+		return math.IsInf(float64(x), 0)
+	case cc.Float64Value:
+		return math.IsInf(float64(x), 0)
+	default:
+		return false
+	}
+}
+
+func (p *project) castExpression(f *function, n *cc.CastExpression, t cc.Type, mode exprMode, flags flags) {
+	if n.Case == cc.CastExpressionCast {
+		if f != nil && n.CastExpression.Operand.Type().Kind() == cc.Ptr { // void *__ccgo_va_arg(__builtin_va_list ap);
+			sv := f.vaType
+			f.vaType = n.TypeName.Type()
+			defer func() { f.vaType = sv }()
+		}
+	}
+	switch mode {
+	case exprValue:
+		p.castExpressionValue(f, n, t, mode, flags)
+	case exprVoid:
+		p.castExpressionVoid(f, n, t, mode, flags)
+	case exprAddrOf:
+		p.castExpressionAddrOf(f, n, t, mode, flags)
+	case exprBool:
+		p.castExpressionBool(f, n, t, mode, flags)
+	case exprLValue:
+		p.castExpressionLValue(f, n, t, mode, flags)
+	case exprPSelect:
+		p.castExpressionPSelect(f, n, t, mode, flags)
+	case exprFunc:
+		p.castExpressionFunc(f, n, t, mode, flags)
+	case exprSelect:
+		p.castExpressionSelect(f, n, t, mode, flags)
+	case exprDecay:
+		p.castExpressionDecay(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), mode))
+	}
+}
+
+func (p *project) castExpressionDecay(f *function, n *cc.CastExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.CastExpressionUnary: // UnaryExpression
+		p.unaryExpression(f, n.UnaryExpression, t, mode, flags)
+	case cc.CastExpressionCast: // '(' TypeName ')' CastExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) castExpressionSelect(f *function, n *cc.CastExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.CastExpressionUnary: // UnaryExpression
+		p.unaryExpression(f, n.UnaryExpression, t, mode, flags)
+	case cc.CastExpressionCast: // '(' TypeName ')' CastExpression
+		p.castExpression(f, n.CastExpression, n.TypeName.Type(), mode, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) castExpressionFunc(f *function, n *cc.CastExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.CastExpressionUnary: // UnaryExpression
+		p.unaryExpression(f, n.UnaryExpression, t, mode, flags)
+	case cc.CastExpressionCast: // '(' TypeName ')' CastExpression
+		ot := n.CastExpression.Operand.Type()
+		tn := n.TypeName.Type()
+		var ft cc.Type
+		switch tn.Kind() {
+		case cc.Ptr:
+			switch et := ot.Elem(); et.Kind() {
+			case cc.Function, cc.Void:
+				// ok
+			default:
+				panic(todo("", p.pos(n), et, et.Kind()))
+			}
+		default:
+			panic(todo("%v: %v, %v -> %v, %v -> %v, %v", p.pos(n), ot, ot.Kind(), tn, tn.Kind(), t, t.Kind()))
+		}
+		switch t.Kind() {
+		case cc.Ptr:
+			switch et := t.Elem(); et.Kind() {
+			case cc.Function:
+				ft = et
+			default:
+				panic(todo("", p.pos(n), et, et.Kind()))
+			}
+		default:
+			panic(todo("%v: %v, %v -> %v, %v -> %v, %v", p.pos(n), ot, ot.Kind(), tn, tn.Kind(), t, t.Kind()))
+		}
+		switch ot.Kind() {
+		case cc.Ptr:
+			switch et := ot.Elem(); et.Kind() {
+			case cc.Function, cc.Void:
+				p.w("(*(*")
+				p.functionSignature(n, f, ft, "")
+				p.w(")(unsafe.Pointer(")
+				p.castExpression(f, n.CastExpression, ot, exprAddrOf, flags)
+				p.w(")))")
+			default:
+				panic(todo("", p.pos(n), et, et.Kind()))
+			}
+		default:
+			panic(todo("%v: %v, %v -> %v, %v -> %v, %v", p.pos(n), ot, ot.Kind(), tn, tn.Kind(), t, t.Kind()))
+		}
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) castExpressionPSelect(f *function, n *cc.CastExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.CastExpressionUnary: // UnaryExpression
+		p.unaryExpression(f, n.UnaryExpression, t, mode, flags)
+	case cc.CastExpressionCast: // '(' TypeName ')' CastExpression
+		p.castExpression(f, n.CastExpression, n.TypeName.Type(), mode, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) castExpressionLValue(f *function, n *cc.CastExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.CastExpressionUnary: // UnaryExpression
+		p.unaryExpression(f, n.UnaryExpression, t, mode, flags)
+	case cc.CastExpressionCast: // '(' TypeName ')' CastExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) castExpressionBool(f *function, n *cc.CastExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.CastExpressionUnary: // UnaryExpression
+		p.unaryExpression(f, n.UnaryExpression, t, mode, flags)
+	case cc.CastExpressionCast: // '(' TypeName ')' CastExpression
+		p.w("(")
+		defer p.w(")")
+		defer p.w(" != 0 ")
+		p.castExpression(f, n, n.Operand.Type(), exprValue, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) castExpressionAddrOf(f *function, n *cc.CastExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.CastExpressionUnary: // UnaryExpression
+		p.unaryExpression(f, n.UnaryExpression, t, mode, flags)
+	case cc.CastExpressionCast: // '(' TypeName ')' CastExpression
+		p.castExpressionAddrOf(f, n.CastExpression, t, mode, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) castExpressionVoid(f *function, n *cc.CastExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.CastExpressionUnary: // UnaryExpression
+		p.unaryExpression(f, n.UnaryExpression, t, mode, flags)
+	case cc.CastExpressionCast: // '(' TypeName ')' CastExpression
+		p.castExpression(f, n.CastExpression, t, mode, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) castExpressionValue(f *function, n *cc.CastExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.CastExpressionUnary: // UnaryExpression
+		p.unaryExpression(f, n.UnaryExpression, t, mode, flags)
+	case cc.CastExpressionCast: // '(' TypeName ')' CastExpression
+		switch k := p.opKind(f, n.CastExpression, n.CastExpression.Operand.Type()); k {
+		case opNormal, opBitfield:
+			p.castExpressionValueNormal(f, n, t, mode, flags)
+		case opArray:
+			p.castExpressionValueArray(f, n, t, mode, flags)
+		case opFunction:
+			p.castExpressionValueFunction(f, n, t, mode, flags)
+		case opArrayParameter:
+			p.castExpressionValueNormal(f, n, t, mode, flags)
+		default:
+			panic(todo("", n.Position(), k))
+		}
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) castExpressionValueArrayParameter(f *function, n *cc.CastExpression, t cc.Type, mode exprMode, flags flags) {
+	// '(' TypeName ')' CastExpression
+	tn := n.TypeName.Type()
+	defer p.w("%s", p.convertType(n, tn, t, flags))
+	p.castExpression(f, n.CastExpression, tn, mode, flags)
+}
+
+func (p *project) castExpressionValueFunction(f *function, n *cc.CastExpression, t cc.Type, mode exprMode, flags flags) {
+	// '(' TypeName ')' CastExpression
+	op := n.CastExpression.Operand
+	tn := n.TypeName.Type()
+	switch {
+	case op.Type().Kind() == cc.Function:
+		switch {
+		case tn.Kind() == cc.Ptr && t.Kind() == cc.Ptr:
+			p.castExpression(f, n.CastExpression, op.Type(), exprValue, flags)
+		case tn.IsIntegerType():
+			p.w("%s(", p.typ(n, tn))
+			p.castExpression(f, n.CastExpression, op.Type(), exprValue, flags)
+			p.w(")")
+		default:
+			panic(todo("%v: tn %v expr %v", n.Position(), tn, op.Type()))
+		}
+	default:
+		panic(todo("%v: %v -> %v -> %v", p.pos(n), op.Type(), tn, t))
+	}
+}
+
+func (p *project) castExpressionValueArray(f *function, n *cc.CastExpression, t cc.Type, mode exprMode, flags flags) {
+	// '(' TypeName ')' CastExpression
+	tn := n.TypeName.Type()
+	switch {
+	case tn.IsScalarType():
+		defer p.w("%s", p.convertType(n, nil, t, flags))
+		p.castExpression(f, n.CastExpression, tn, exprDecay, flags)
+	default:
+		panic(todo("", p.pos(n)))
+	}
+}
+
+func (p *project) castExpressionValueNormal(f *function, n *cc.CastExpression, t cc.Type, mode exprMode, flags flags) {
+	// '(' TypeName ')' CastExpression
+	op := n.CastExpression.Operand
+	tn := n.TypeName.Type()
+	switch {
+	case op.Type().Kind() == cc.Ptr && tn.IsArithmeticType():
+		defer p.w("%s", p.convertType(n, nil, t, flags|fForceConv))
+		p.castExpression(f, n.CastExpression, op.Type(), mode, flags)
+	case tn.IsArithmeticType():
+		switch {
+		case (tn.Kind() == cc.Float || tn.Kind() == cc.Double) && op.Type().IsIntegerType() && op.Value() != nil && t.IsIntegerType():
+			panic(todo("", p.pos(n)))
+		case isNegativeInt(op) && isUnsigned(t):
+			defer p.w("%s", p.convertType(n, tn, t, flags|fForceConv))
+			p.castExpression(f, n.CastExpression, tn, exprValue, flags)
+		default:
+			defer p.w("%s", p.convertType(n, tn, t, flags))
+			p.castExpression(f, n.CastExpression, tn, exprValue, flags)
+		}
+	default:
+		switch tn.Kind() {
+		case cc.Ptr:
+			switch {
+			case t.Kind() == cc.Ptr && isNegativeInt(op):
+				p.w("%s(", p.helperType2(n, op.Type(), tn))
+				defer p.w(")")
+				p.castExpression(f, n.CastExpression, op.Type(), mode, flags)
+			default:
+				defer p.w("%s", p.convertType(n, tn, t, flags))
+				p.castExpression(f, n.CastExpression, tn, mode, flags)
+			}
+		case cc.Void:
+			p.castExpression(f, n.CastExpression, tn, exprVoid, flags)
+		default:
+			panic(todo("%s: %s %s -> %s %s -> %s %s", n.Position(), op.Type(), op.Type().Kind(), tn, tn.Kind(), t, t.Kind()))
+		}
+	}
+}
+
+func (p *project) unaryExpression(f *function, n *cc.UnaryExpression, t cc.Type, mode exprMode, flags flags) {
+	switch mode {
+	case exprLValue:
+		p.unaryExpressionLValue(f, n, t, mode, flags)
+	case exprValue:
+		p.unaryExpressionValue(f, n, t, mode, flags)
+	case exprVoid:
+		p.unaryExpressionVoid(f, n, t, mode, flags)
+	case exprAddrOf:
+		p.unaryExpressionAddrOf(f, n, t, mode, flags)
+	case exprBool:
+		p.unaryExpressionBool(f, n, t, mode, flags)
+	case exprPSelect:
+		p.unaryExpressionPSelect(f, n, t, mode, flags)
+	case exprFunc:
+		p.unaryExpressionFunc(f, n, t, mode, flags)
+	case exprSelect:
+		p.unaryExpressionSelect(f, n, t, mode, flags)
+	case exprDecay:
+		p.unaryExpressionDecay(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), mode))
+	}
+}
+
+func (p *project) unaryExpressionDecay(f *function, n *cc.UnaryExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.UnaryExpressionPostfix: // PostfixExpression
+		p.postfixExpression(f, n.PostfixExpression, t, mode, flags)
+	case cc.UnaryExpressionInc: // "++" UnaryExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionDec: // "--" UnaryExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionAddrof: // '&' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionDeref: // '*' CastExpression
+		p.castExpression(f, n.CastExpression, n.CastExpression.Operand.Type(), exprValue, flags)
+	case cc.UnaryExpressionPlus: // '+' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionMinus: // '-' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionCpl: // '~' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionNot: // '!' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionSizeofExpr: // "sizeof" UnaryExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionSizeofType: // "sizeof" '(' TypeName ')'
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionLabelAddr: // "&&" IDENTIFIER
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionAlignofExpr: // "_Alignof" UnaryExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionAlignofType: // "_Alignof" '(' TypeName ')'
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionImag: // "__imag__" UnaryExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionReal: // "__real__" UnaryExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) unaryExpressionSelect(f *function, n *cc.UnaryExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.UnaryExpressionPostfix: // PostfixExpression
+		p.postfixExpression(f, n.PostfixExpression, t, mode, flags)
+	case cc.UnaryExpressionInc: // "++" UnaryExpression
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionDec: // "--" UnaryExpression
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionAddrof: // '&' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionDeref: // '*' CastExpression
+		ot := n.CastExpression.Operand.Type()
+		switch ot.Kind() {
+		case cc.Ptr:
+			switch et := ot.Elem(); et.Kind() {
+			case
+				cc.Struct,
+				cc.Union:
+
+				p.w("(*(*%s)(unsafe.Pointer(", p.typ(n, n.Operand.Type()))
+				p.castExpression(f, n.CastExpression, n.CastExpression.Operand.Type(), exprValue, flags)
+				p.w(")))")
+			default:
+				panic(todo("", p.pos(n), et, et.Kind()))
+			}
+		case cc.Array:
+			p.w("(*(*%s)(unsafe.Pointer(", p.typ(n, n.Operand.Type()))
+			p.castExpression(f, n.CastExpression, n.CastExpression.Operand.Type(), exprAddrOf, flags)
+			p.w(")))")
+		default:
+			panic(todo("", p.pos(n), ot, ot.Kind()))
+		}
+	case cc.UnaryExpressionPlus: // '+' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionMinus: // '-' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionCpl: // '~' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionNot: // '!' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionSizeofExpr: // "sizeof" UnaryExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionSizeofType: // "sizeof" '(' TypeName ')'
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionLabelAddr: // "&&" IDENTIFIER
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionAlignofExpr: // "_Alignof" UnaryExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionAlignofType: // "_Alignof" '(' TypeName ')'
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionImag: // "__imag__" UnaryExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionReal: // "__real__" UnaryExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) unaryExpressionFunc(f *function, n *cc.UnaryExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.UnaryExpressionPostfix: // PostfixExpression
+		p.postfixExpression(f, n.PostfixExpression, t, mode, flags)
+	case cc.UnaryExpressionInc: // "++" UnaryExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionDec: // "--" UnaryExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionAddrof: // '&' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionDeref: // '*' CastExpression
+		ot := n.CastExpression.Operand.Type()
+		switch ot.Kind() {
+		case cc.Ptr:
+			switch et := ot.Elem(); et.Kind() {
+			case cc.Function:
+				p.castExpression(f, n.CastExpression, ot, mode, flags|fAddrOfFuncPtrOk)
+			case cc.Ptr:
+				switch et2 := et.Elem(); et2.Kind() {
+				case cc.Function:
+					// C: (**)()
+					p.fnVal(n, f, func() { p.castExpression(f, n.CastExpression, p.ptrType, exprValue, flags|fAddrOfFuncPtrOk) }, n.CastExpression.Declarator(), n.CastExpression.Operand.Type(), 1, mode, flags)
+				default:
+					panic(todo("", p.pos(n), et2, et2.Kind()))
+				}
+			default:
+				panic(todo("", p.pos(n), et, et.Kind()))
+			}
+		case cc.Function:
+			p.castExpression(f, n.CastExpression, ot, mode, flags|fAddrOfFuncPtrOk)
+		default:
+			panic(todo("", p.pos(n), ot, ot.Kind(), mode))
+		}
+	case cc.UnaryExpressionPlus: // '+' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionMinus: // '-' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionCpl: // '~' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionNot: // '!' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionSizeofExpr: // "sizeof" UnaryExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionSizeofType: // "sizeof" '(' TypeName ')'
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionLabelAddr: // "&&" IDENTIFIER
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionAlignofExpr: // "_Alignof" UnaryExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionAlignofType: // "_Alignof" '(' TypeName ')'
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionImag: // "__imag__" UnaryExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionReal: // "__real__" UnaryExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) unaryExpressionPSelect(f *function, n *cc.UnaryExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.UnaryExpressionPostfix: // PostfixExpression
+		p.postfixExpression(f, n.PostfixExpression, t, mode, flags)
+	case cc.UnaryExpressionInc: // "++" UnaryExpression
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionDec: // "--" UnaryExpression
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionAddrof: // '&' CastExpression
+		panic(todo("", n.Position()))
+		//TODO- p.w("(*%s)(unsafe.Pointer(", p.typ(n, n.Operand.Type().Elem()))
+		//TODO- p.unaryExpression(f, n, t, exprValue, flags)
+		//TODO- p.w("))")
+	case cc.UnaryExpressionDeref: // '*' CastExpression
+		panic(todo("", n.Position()))
+		//TODO- ot := n.CastExpression.Operand.Type()
+		//TODO- switch ot.Kind() {
+		//TODO- case cc.Ptr:
+		//TODO- 	switch et := ot.Elem(); {
+		//TODO- 	case et.Kind() == cc.Ptr:
+		//TODO- 		switch et2 := et.Elem(); et2.Kind() {
+		//TODO- 		case cc.Struct:
+		//TODO- 			if et2.IsIncomplete() {
+		//TODO- 				p.w("(*(**uintptr)(unsafe.Pointer(")
+		//TODO- 				p.castExpression(f, n.CastExpression, t, exprValue, flags)
+		//TODO- 				p.w(")))")
+		//TODO- 				break
+		//TODO- 			}
+
+		//TODO- 			p.w("(*(**%s)(unsafe.Pointer(", p.typ(n, n.Operand.Type().Elem()))
+		//TODO- 			p.castExpression(f, n.CastExpression, t, exprValue, flags)
+		//TODO- 			p.w(")))")
+		//TODO- 		default:
+		//TODO- 			panic(todo("", p.pos(n), et2, et2.Kind()))
+		//TODO- 		}
+		//TODO- 	default:
+		//TODO- 		panic(todo("", p.pos(n), et, et.Kind()))
+		//TODO- 	}
+		//TODO- default:
+		//TODO- 	panic(todo("", p.pos(n), ot, ot.Kind()))
+		//TODO- }
+	case cc.UnaryExpressionPlus: // '+' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionMinus: // '-' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionCpl: // '~' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionNot: // '!' CastExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionSizeofExpr: // "sizeof" UnaryExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionSizeofType: // "sizeof" '(' TypeName ')'
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionLabelAddr: // "&&" IDENTIFIER
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionAlignofExpr: // "_Alignof" UnaryExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionAlignofType: // "_Alignof" '(' TypeName ')'
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionImag: // "__imag__" UnaryExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionReal: // "__real__" UnaryExpression
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) unaryExpressionBool(f *function, n *cc.UnaryExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.UnaryExpressionPostfix: // PostfixExpression
+		p.postfixExpression(f, n.PostfixExpression, t, mode, flags)
+	case cc.UnaryExpressionNot: // '!' CastExpression
+		p.w("!(")
+		p.castExpression(f, n.CastExpression, t, mode, flags)
+		p.w(")")
+	default:
+		p.w("(")
+		defer p.w(")")
+		defer p.w(" != 0 ")
+		p.unaryExpression(f, n, t, exprValue, flags)
+	}
+}
+
+func (p *project) unaryExpressionAddrOf(f *function, n *cc.UnaryExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.UnaryExpressionPostfix: // PostfixExpression
+		p.postfixExpression(f, n.PostfixExpression, t, mode, flags)
+	case cc.UnaryExpressionInc: // "++" UnaryExpression
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionDec: // "--" UnaryExpression
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionAddrof: // '&' CastExpression
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionDeref: // '*' CastExpression
+		ot := n.CastExpression.Operand.Type()
+		switch ot.Kind() {
+		case cc.Ptr:
+			switch et := ot.Elem(); {
+			case
+				et.IsScalarType(),
+				et.Kind() == cc.Struct,
+				et.Kind() == cc.Union,
+				et.Kind() == cc.Array:
+
+				p.unaryExpressionDeref(f, n, t, mode, flags)
+			default:
+				panic(todo("", p.pos(n), et, et.Kind()))
+			}
+		default:
+			panic(todo("", p.pos(n), ot, ot.Kind()))
+		}
+	case cc.UnaryExpressionPlus: // '+' CastExpression
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionMinus: // '-' CastExpression
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionCpl: // '~' CastExpression
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionNot: // '!' CastExpression
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionSizeofExpr: // "sizeof" UnaryExpression
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionSizeofType: // "sizeof" '(' TypeName ')'
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionLabelAddr: // "&&" IDENTIFIER
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionAlignofExpr: // "_Alignof" UnaryExpression
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionAlignofType: // "_Alignof" '(' TypeName ')'
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionImag: // "__imag__" UnaryExpression
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionReal: // "__real__" UnaryExpression
+		panic(todo("", n.Position()))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) unaryExpressionVoid(f *function, n *cc.UnaryExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.UnaryExpressionPostfix: // PostfixExpression
+		p.postfixExpression(f, n.PostfixExpression, t, mode, flags)
+	case cc.UnaryExpressionInc: // "++" UnaryExpression
+		p.unaryExpressionPreIncDec(f, n, "++", "+=", t, mode, flags)
+	case cc.UnaryExpressionDec: // "--" UnaryExpression
+		p.unaryExpressionPreIncDec(f, n, "--", "-=", t, mode, flags)
+	case cc.UnaryExpressionAddrof: // '&' CastExpression
+		p.w("_ = ")
+		switch {
+		case n.CastExpression.Operand.Type().Kind() == cc.Array:
+			panic(todo("", p.pos(n)))
+		default:
+			p.castExpression(f, n.CastExpression, n.CastExpression.Operand.Type(), exprAddrOf, flags)
+		}
+	case cc.UnaryExpressionDeref: // '*' CastExpression
+		p.w("_ = *(*byte)(unsafe.Pointer(")
+		p.castExpression(f, n.CastExpression, n.CastExpression.Operand.Type(), exprValue, flags)
+		p.w("))")
+	case
+		cc.UnaryExpressionPlus,  // '+' CastExpression
+		cc.UnaryExpressionMinus, // '-' CastExpression
+		cc.UnaryExpressionNot,   // '!' CastExpression
+		cc.UnaryExpressionCpl:   // '~' CastExpression
+
+		p.w("_ = ")
+		defer p.w("%s", p.convert(n, n.CastExpression.Operand, p.intType, flags))
+		p.castExpression(f, n.CastExpression, n.CastExpression.Operand.Type(), exprValue, flags)
+	case cc.UnaryExpressionSizeofExpr: // "sizeof" UnaryExpression
+		// nop
+	case cc.UnaryExpressionSizeofType: // "sizeof" '(' TypeName ')'
+		// nop
+	case cc.UnaryExpressionLabelAddr: // "&&" IDENTIFIER
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionAlignofExpr: // "_Alignof" UnaryExpression
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionAlignofType: // "_Alignof" '(' TypeName ')'
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionImag: // "__imag__" UnaryExpression
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionReal: // "__real__" UnaryExpression
+		panic(todo("", n.Position()))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) unaryExpressionValue(f *function, n *cc.UnaryExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.UnaryExpressionPostfix: // PostfixExpression
+		p.postfixExpression(f, n.PostfixExpression, t, mode, flags)
+	case cc.UnaryExpressionInc: // "++" UnaryExpression
+		p.unaryExpressionPreIncDec(f, n, "++", "+=", t, mode, flags)
+	case cc.UnaryExpressionDec: // "--" UnaryExpression
+		p.unaryExpressionPreIncDec(f, n, "--", "-=", t, mode, flags)
+	case cc.UnaryExpressionAddrof: // '&' CastExpression
+		if t.Kind() != cc.Ptr {
+			defer p.w("%s", p.convert(n, n.Operand, t, flags))
+		}
+		switch {
+		case n.CastExpression.Operand.Type().Kind() == cc.Array:
+			panic(todo("", p.pos(n)))
+		default:
+			p.castExpression(f, n.CastExpression, n.CastExpression.Operand.Type(), exprAddrOf, flags)
+		}
+	case cc.UnaryExpressionDeref: // '*' CastExpression
+		ot := n.CastExpression.Operand.Type()
+		switch ot.Kind() {
+		case cc.Ptr, cc.Array:
+			switch et := ot.Elem(); {
+			case
+				et.IsScalarType(),
+				et.Kind() == cc.Array,
+				et.Kind() == cc.Struct,
+				et.Kind() == cc.Union:
+
+				p.unaryExpressionDeref(f, n, t, mode, flags)
+			case et.Kind() == cc.Function:
+				p.castExpression(f, n.CastExpression, t, mode, flags)
+			default:
+				panic(todo("", p.pos(n), et, et.Kind()))
+			}
+		default:
+			panic(todo("", p.pos(n), ot, ot.Kind()))
+		}
+	case cc.UnaryExpressionPlus: // '+' CastExpression
+		p.w(" +")
+		p.castExpression(f, n.CastExpression, t, mode, flags)
+	case cc.UnaryExpressionMinus: // '-' CastExpression
+		switch {
+		case isNonNegativeInt(n.CastExpression.Operand) && t.Kind() == cc.Ptr:
+			p.w(" -%sUintptr(", p.task.crt)
+			p.castExpression(f, n.CastExpression, n.CastExpression.Operand.Type(), exprValue, flags)
+			p.w(")")
+		case isZeroReal(n.CastExpression.Operand):
+			p.w(" -")
+			defer p.w("%s", p.convert(n, n.CastExpression.Operand, t, flags))
+			p.w("%s%sFrom%[2]s(", p.task.crt, p.helperType(n, n.CastExpression.Operand.Type()))
+			p.castExpression(f, n.CastExpression, n.CastExpression.Operand.Type(), exprValue, flags)
+			p.w(")")
+		case isNonNegativeInt(n.CastExpression.Operand) && isUnsigned(n.Operand.Type()):
+			defer p.w("%s", p.convert(n, n.CastExpression.Operand, t, flags))
+			p.w("%sNeg%s(", p.task.crt, p.helperType(n, n.CastExpression.Operand.Type()))
+			p.castExpression(f, n.CastExpression, n.CastExpression.Operand.Type(), exprValue, flags)
+			p.w(")")
+		default:
+			defer p.w("%s", p.convert(n, n.Operand, t, flags))
+			p.w(" -")
+			p.castExpression(f, n.CastExpression, n.Operand.Type(), exprValue, flags)
+		}
+	case cc.UnaryExpressionCpl: // '~' CastExpression
+		defer p.w("%s", p.convert(n, n.Operand, t, flags))
+		switch {
+		case n.CastExpression.Operand.Value() != nil:
+			switch {
+			case !t.IsIntegerType():
+				p.w(" ^")
+				p.castExpression(f, n.CastExpression, n.Operand.Type(), exprValue, flags|fForceRuntimeConv)
+			default:
+				p.w("%sCpl%s(", p.task.crt, p.helperType(n, n.Operand.Type()))
+				p.castExpression(f, n.CastExpression, n.Operand.Type(), exprValue, flags)
+				p.w(")")
+			}
+		default:
+			p.w(" ^")
+			p.castExpression(f, n.CastExpression, n.Operand.Type(), exprValue, flags)
+		}
+	case cc.UnaryExpressionNot: // '!' CastExpression
+		p.w("%sBool%s(!(", p.task.crt, p.helperType(n, t))
+		p.castExpression(f, n.CastExpression, n.CastExpression.Operand.Type(), exprBool, flags)
+		p.w("))")
+	case cc.UnaryExpressionSizeofExpr: // "sizeof" UnaryExpression
+		p.checkSizeof(n.UnaryExpression, n.UnaryExpression.Operand.Type())
+		defer p.w("%s", p.convertNil(n, t, flags))
+		if d := n.UnaryExpression.Declarator(); d != nil {
+			var isLocal bool
+			if f != nil {
+				if local := f.locals[d]; local != nil {
+					isLocal = true
+					if !local.isPinned {
+						p.w("unsafe.Sizeof(%s)", local.name)
+						return
+					}
+				}
+			}
+
+			if !isLocal {
+				if tld := p.tlds[d]; tld != nil {
+					p.w("unsafe.Sizeof(%s)", tld.name)
+					break
+				}
+
+				nm := d.Name().String()
+				if imp := p.imports[nm]; imp != nil {
+					imp.used = true
+					p.w("unsafe.Sizeof(%sX%s)", imp.qualifier, nm)
+					break
+				}
+			}
+		}
+
+		t := n.UnaryExpression.Operand.Type()
+		if p.isArray(f, n.UnaryExpression, t) {
+			p.w("%d", t.Len()*t.Elem().Size())
+			break
+		}
+
+		s := "(0)"
+		if !t.IsArithmeticType() {
+			switch t.Kind() {
+			case cc.Ptr:
+				// ok
+			case cc.Struct, cc.Union, cc.Array:
+				s = "{}"
+			default:
+				panic(todo("", t.Kind()))
+			}
+		}
+		switch t.Kind() {
+		case cc.Int128, cc.UInt128:
+			s = "{}"
+		}
+		p.w("unsafe.Sizeof(%s%s)", p.typ(n, t), s)
+	case cc.UnaryExpressionSizeofType: // "sizeof" '(' TypeName ')'
+		defer p.w("%s", p.convertNil(n, t, flags))
+		t := n.TypeName.Type()
+		p.checkSizeof(n.TypeName, t)
+		if t.Kind() == cc.Array {
+			p.w("%d", t.Len()*t.Elem().Size())
+			break
+		}
+
+		s := "(0)"
+		if !t.IsArithmeticType() {
+			switch t.Kind() {
+			case cc.Ptr:
+				// ok
+			case cc.Struct, cc.Union:
+				s = "{}"
+			default:
+				panic(todo("", t.Kind()))
+			}
+		}
+		switch t.Kind() {
+		case cc.Int128, cc.UInt128:
+			s = "{}"
+		}
+		p.w("unsafe.Sizeof(%s%s)", p.typ(n, t), s)
+	case cc.UnaryExpressionLabelAddr: // "&&" IDENTIFIER
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionAlignofExpr: // "_Alignof" UnaryExpression
+		if n.TypeName.Type().Kind() == cc.Void {
+			p.intConst(n, "", n.Operand, t, flags)
+			break
+		}
+
+		defer p.w("%s", p.convertNil(n, t, flags))
+		t := n.UnaryExpression.Operand.Type()
+		if p.isArray(f, n.UnaryExpression, t) {
+			p.w("%d", t.Len()*t.Elem().Size())
+			break
+		}
+
+		s := "(0)"
+		if !t.IsArithmeticType() {
+			switch t.Kind() {
+			case cc.Ptr:
+				// ok
+			case cc.Struct, cc.Union:
+				s = "{}"
+			default:
+				panic(todo("", t.Kind()))
+			}
+		}
+		p.w("unsafe.Alignof(%s%s)", p.typ(n, t), s)
+	case cc.UnaryExpressionAlignofType: // "_Alignof" '(' TypeName ')'
+		if n.TypeName.Type().Kind() == cc.Void {
+			p.intConst(n, "", n.Operand, t, flags)
+			break
+		}
+
+		defer p.w("%s", p.convertNil(n, t, flags))
+		t := n.TypeName.Type()
+		if t.Kind() == cc.Array {
+			p.w("%d", t.Len()*t.Elem().Size())
+			break
+		}
+
+		s := "(0)"
+		if !t.IsArithmeticType() {
+			switch t.Kind() {
+			case cc.Ptr:
+				// ok
+			case cc.Struct, cc.Union:
+				s = "{}"
+			default:
+				panic(todo("", t.Kind()))
+			}
+		}
+		p.w("unsafe.Alignof(%s%s)", p.typ(n, t), s)
+	case cc.UnaryExpressionImag: // "__imag__" UnaryExpression
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionReal: // "__real__" UnaryExpression
+		panic(todo("", n.Position()))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) checkSizeof(n cc.Node, t cc.Type) {
+	if !p.checkSizeof0(n, t) {
+		p.err(n, "sizeof type %s: not supported", t.Alias())
+	}
+}
+
+func (p *project) checkSizeof0(n cc.Node, t cc.Type) (ok bool) {
+	switch t.Kind() {
+	case cc.Array:
+		return !t.IsVLA()
+	case cc.Struct, cc.Union:
+		nf := t.NumField()
+		for i := []int{0}; i[0] < nf; i[0]++ {
+			if !p.checkSizeof0(n, t.FieldByIndex(i).Type()) {
+				return false
+			}
+		}
+	}
+	return true
+}
+
+func (p *project) unaryExpressionLValue(f *function, n *cc.UnaryExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.UnaryExpressionPostfix: // PostfixExpression
+		p.postfixExpression(f, n.PostfixExpression, t, mode, flags)
+	case cc.UnaryExpressionInc: // "++" UnaryExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionDec: // "--" UnaryExpression
+		panic(todo("", p.pos(n)))
+	case cc.UnaryExpressionAddrof: // '&' CastExpression
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionDeref: // '*' CastExpression
+		ot := n.CastExpression.Operand.Type()
+		switch ot.Kind() {
+		case cc.Ptr, cc.Array:
+			switch et := ot.Elem(); {
+			case
+				et.IsScalarType(),
+				et.Kind() == cc.Struct,
+				et.Kind() == cc.Union:
+
+				p.unaryExpressionDeref(f, n, t, mode, flags)
+			default:
+				panic(todo("", p.pos(n), et, et.Kind()))
+			}
+		default:
+			panic(todo("", p.pos(n), ot, ot.Kind()))
+		}
+	case cc.UnaryExpressionPlus: // '+' CastExpression
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionMinus: // '-' CastExpression
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionCpl: // '~' CastExpression
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionNot: // '!' CastExpression
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionSizeofExpr: // "sizeof" UnaryExpression
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionSizeofType: // "sizeof" '(' TypeName ')'
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionLabelAddr: // "&&" IDENTIFIER
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionAlignofExpr: // "_Alignof" UnaryExpression
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionAlignofType: // "_Alignof" '(' TypeName ')'
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionImag: // "__imag__" UnaryExpression
+		panic(todo("", n.Position()))
+	case cc.UnaryExpressionReal: // "__real__" UnaryExpression
+		panic(todo("", n.Position()))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func isSigned(t cc.Type) bool   { return t.IsIntegerType() && t.IsSignedType() }
+func isUnsigned(t cc.Type) bool { return t.IsIntegerType() && !t.IsSignedType() }
+
+func isConstInteger(op cc.Operand) bool {
+	switch op.Value().(type) {
+	case cc.Int64Value, cc.Uint64Value:
+		return true
+	default:
+		return false
+	}
+}
+
+func isNegativeInt(op cc.Operand) bool {
+	switch x := op.Value().(type) {
+	case cc.Int64Value:
+		return x < 0
+	default:
+		return false
+	}
+}
+
+func isNonNegativeInt(op cc.Operand) bool {
+	switch x := op.Value().(type) {
+	case cc.Int64Value:
+		return x >= 0
+	case cc.Uint64Value:
+		return true
+	default:
+		return false
+	}
+}
+
+func (p *project) unaryExpressionPreIncDec(f *function, n *cc.UnaryExpression, oper, oper2 string, t cc.Type, mode exprMode, flags flags) {
+	// "++" UnaryExpression etc.
+	switch mode {
+	case exprValue:
+		p.unaryExpressionPreIncDecValue(f, n, oper, oper2, t, mode, flags)
+	case exprVoid:
+		p.unaryExpressionPreIncDecVoid(f, n, oper, oper2, t, mode, flags)
+	default:
+		panic(todo("", p.pos(n), mode))
+	}
+}
+
+func (p *project) unaryExpressionPreIncDecVoid(f *function, n *cc.UnaryExpression, oper, oper2 string, t cc.Type, mode exprMode, flags flags) {
+	// "++" UnaryExpression etc.
+	switch n.UnaryExpression.Operand.Type().Kind() {
+	case cc.Int128, cc.UInt128:
+		p.unaryExpressionLValue(f, n.UnaryExpression, n.UnaryExpression.Operand.Type(), exprLValue, 0)
+		switch oper {
+		case "++":
+			p.w(".LValueInc()")
+		case "--":
+			p.w(".LValueDec()")
+		default:
+			panic(todo("internal error: %q", oper))
+		}
+		return
+	}
+
+	// "++" UnaryExpression etc.
+	switch k := p.opKind(f, n.UnaryExpression, n.UnaryExpression.Operand.Type()); k {
+	case opNormal:
+		p.unaryExpressionPreIncDecVoidNormal(f, n, oper, oper2, t, mode, flags)
+	case opArrayParameter:
+		p.unaryExpressionPreIncDecVoidArrayParameter(f, n, oper, oper2, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), k))
+	}
+}
+
+func (p *project) unaryExpressionPreIncDecVoidArrayParameter(f *function, n *cc.UnaryExpression, oper, oper2 string, t cc.Type, mode exprMode, flags flags) {
+	// "++" UnaryExpression etc.
+	ut := n.UnaryExpression.Operand.Type()
+	p.unaryExpression(f, n.UnaryExpression, n.UnaryExpression.Operand.Type(), exprLValue, flags)
+	switch d := p.incDelta(n, ut); d {
+	case 1:
+		p.w("%s", oper)
+	default:
+		p.w("%s %d", oper2, d)
+	}
+}
+
+func (p *project) unaryExpressionPreIncDecVoidNormal(f *function, n *cc.UnaryExpression, oper, oper2 string, t cc.Type, mode exprMode, flags flags) {
+	// "++" UnaryExpression etc.
+	ut := n.UnaryExpression.Operand.Type()
+	if d := n.UnaryExpression.Declarator(); d != nil && p.isVolatileOrAtomic(d) {
+		x := "Dec"
+		if oper == "++" {
+			x = "Inc"
+		}
+		p.w("%sPre%sAtomic%s(&", p.task.crt, x, p.helperType(n, d.Type()))
+		switch local, tld := f.locals[d], p.tlds[d]; {
+		case local != nil:
+			p.w("%s", local.name)
+		case tld != nil:
+			p.w("%s", tld.name)
+		default:
+			panic(todo(""))
+		}
+		p.w(", %d)", p.incDelta(n.PostfixExpression, ut))
+		return
+	}
+
+	p.unaryExpression(f, n.UnaryExpression, n.UnaryExpression.Operand.Type(), exprLValue, flags)
+	if ut.IsIntegerType() || ut.Kind() == cc.Ptr && p.incDelta(n, ut) == 1 {
+		p.w("%s", oper)
+		return
+	}
+
+	switch ut.Kind() {
+	case cc.Ptr, cc.Double, cc.Float:
+		p.w("%s %d", oper2, p.incDelta(n, ut))
+		return
+	}
+
+	panic(todo("", p.pos(n)))
+}
+
+func (p *project) unaryExpressionPreIncDecValue(f *function, n *cc.UnaryExpression, oper, oper2 string, t cc.Type, mode exprMode, flags flags) {
+	// "++" UnaryExpression etc.
+	switch k := p.opKind(f, n.UnaryExpression, n.UnaryExpression.Operand.Type()); k {
+	case opNormal:
+		p.unaryExpressionPreIncDecValueNormal(f, n, oper, oper2, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), k))
+	}
+}
+
+func (p *project) unaryExpressionPreIncDecValueNormal(f *function, n *cc.UnaryExpression, oper, oper2 string, t cc.Type, mode exprMode, flags flags) {
+	// "++" UnaryExpression etc.
+	defer p.w("%s", p.convert(n, n.UnaryExpression.Operand, t, flags))
+	x := "Dec"
+	if oper == "++" {
+		x = "Inc"
+	}
+	ut := n.UnaryExpression.Operand.Type()
+	p.w("%sPre%s%s(&", p.task.crt, x, p.helperType(n, ut))
+	p.unaryExpression(f, n.UnaryExpression, ut, exprLValue, flags)
+	p.w(", %d)", p.incDelta(n.PostfixExpression, ut))
+}
+
+func (p *project) unaryExpressionDeref(f *function, n *cc.UnaryExpression, t cc.Type, mode exprMode, flags flags) {
+	// '*' CastExpression
+	switch mode {
+	case exprValue:
+		p.unaryExpressionDerefValue(f, n, t, mode, flags)
+	case exprLValue:
+		p.unaryExpressionDerefLValue(f, n, t, mode, flags)
+	case exprAddrOf:
+		p.unaryExpressionDerefAddrOf(f, n, t, mode, flags)
+	case exprBool:
+		p.unaryExpressionDerefBool(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), mode))
+	}
+}
+
+func (p *project) unaryExpressionDerefBool(f *function, n *cc.UnaryExpression, t cc.Type, mode exprMode, flags flags) {
+	// '*' CastExpression
+	p.w("(")
+	defer p.w(")")
+	p.w("*(*%s)(unsafe.Pointer(", p.typ(n, n.Operand.Type()))
+	p.castExpression(f, n.CastExpression, n.CastExpression.Operand.Type(), exprValue, flags)
+	p.w(")) != 0")
+}
+
+func (p *project) unaryExpressionDerefAddrOf(f *function, n *cc.UnaryExpression, t cc.Type, mode exprMode, flags flags) {
+	// '*' CastExpression
+	p.castExpression(f, n.CastExpression, n.CastExpression.Operand.Type(), exprValue, flags)
+}
+
+func (p *project) unaryExpressionDerefLValue(f *function, n *cc.UnaryExpression, t cc.Type, mode exprMode, flags flags) {
+	// '*' CastExpression
+	switch k := p.opKind(f, n.CastExpression, n.CastExpression.Operand.Type()); k {
+	case opNormal:
+		p.unaryExpressionDerefLValueNormal(f, n, t, mode, flags)
+	case opArray:
+		panic(todo("", p.pos(n)))
+		p.unaryExpressionDerefLValueArray(f, n, t, mode, flags)
+	case opArrayParameter:
+		p.unaryExpressionDerefLValueNormal(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), k))
+	}
+}
+
+func (p *project) unaryExpressionDerefLValueArray(f *function, n *cc.UnaryExpression, t cc.Type, mode exprMode, flags flags) {
+	defer p.w("))%s", p.convertType(n, n.CastExpression.Operand.Type().Elem(), t, flags))
+	p.w("*(*%s)(unsafe.Pointer(", p.typ(n, n.Operand.Type()))
+	p.castExpression(f, n.CastExpression, n.CastExpression.Operand.Type(), exprValue, flags)
+}
+
+func (p *project) unaryExpressionDerefLValueNormal(f *function, n *cc.UnaryExpression, t cc.Type, mode exprMode, flags flags) {
+	defer p.w("))%s", p.convertType(n, n.CastExpression.Operand.Type().Elem(), t, flags))
+	p.w("*(*%s)(unsafe.Pointer(", p.typ(n, n.Operand.Type()))
+	p.castExpression(f, n.CastExpression, n.CastExpression.Operand.Type(), exprValue, flags)
+}
+
+func (p *project) unaryExpressionDerefValue(f *function, n *cc.UnaryExpression, t cc.Type, mode exprMode, flags flags) {
+	// '*' CastExpression
+	switch k := p.opKind(f, n.CastExpression, n.CastExpression.Operand.Type()); k {
+	case opNormal, opArrayParameter:
+		p.unaryExpressionDerefValueNormal(f, n, t, mode, flags)
+	case opArray:
+		p.unaryExpressionDerefValueArray(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), k))
+	}
+}
+
+func (p *project) unaryExpressionDerefValueArray(f *function, n *cc.UnaryExpression, t cc.Type, mode exprMode, flags flags) {
+	defer p.w("%s", p.convertType(n, n.CastExpression.Operand.Type().Elem(), t, flags))
+	p.castExpression(f, n.CastExpression, n.CastExpression.Operand.Type(), exprValue, flags)
+	p.w("[0]")
+}
+
+func (p *project) unaryExpressionDerefValueNormal(f *function, n *cc.UnaryExpression, t cc.Type, mode exprMode, flags flags) {
+	// '*' CastExpression
+	switch op := n.Operand.Type(); {
+	case op.Kind() == cc.Array:
+		p.castExpression(f, n.CastExpression, n.CastExpression.Operand.Type(), mode, flags)
+	default:
+		defer p.w("))%s", p.convertType(n, n.CastExpression.Operand.Type().Elem(), t, flags))
+		p.w("*(*%s)(unsafe.Pointer(", p.typ(n, n.Operand.Type()))
+		p.castExpression(f, n.CastExpression, n.CastExpression.Operand.Type(), mode, flags)
+	}
+}
+
+func (p *project) postfixExpression(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	switch mode {
+	case exprLValue:
+		p.postfixExpressionLValue(f, n, t, mode, flags)
+	case exprValue:
+		p.postfixExpressionValue(f, n, t, mode, flags)
+	case exprVoid:
+		p.postfixExpressionVoid(f, n, t, mode, flags)
+	case exprFunc:
+		p.postfixExpressionFunc(f, n, t, mode, flags)
+	case exprAddrOf:
+		p.postfixExpressionAddrOf(f, n, t, mode, flags)
+	case exprSelect:
+		p.postfixExpressionSelect(f, n, t, mode, flags)
+	case exprPSelect:
+		p.postfixExpressionPSelect(f, n, t, mode, flags)
+	case exprBool:
+		p.postfixExpressionBool(f, n, t, mode, flags)
+	case exprDecay:
+		p.postfixExpressionDecay(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), mode))
+	}
+}
+
+func (p *project) postfixExpressionDecay(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.PostfixExpressionPrimary: // PrimaryExpression
+		p.primaryExpression(f, n.PrimaryExpression, t, mode, flags)
+	case cc.PostfixExpressionIndex: // PostfixExpression '[' Expression ']'
+		defer p.w("%s", p.convert(n, n.Operand, t, flags))
+		pe := n.PostfixExpression.Operand.Type()
+		p.w("(")
+		switch {
+		case pe.Kind() == cc.Array:
+			p.postfixExpression(f, n.PostfixExpression, pe, exprDecay, flags)
+		case pe.Kind() == cc.Ptr:
+			p.postfixExpression(f, n.PostfixExpression, pe, exprValue, flags)
+		default:
+			panic(todo("", p.pos(n)))
+		}
+		if !n.Expression.Operand.IsZero() {
+			p.nzUintptr(n, func() { p.expression(f, n.Expression, n.Expression.Operand.Type(), exprValue, flags) }, n.Expression.Operand)
+			if sz := pe.Elem().Size(); sz != 1 {
+				p.w("*%d", sz)
+			}
+		}
+		p.w(")")
+	case cc.PostfixExpressionCall: // PostfixExpression '(' ArgumentExpressionList ')'
+		panic(todo("", p.pos(n)))
+	case cc.PostfixExpressionSelect: // PostfixExpression '.' IDENTIFIER
+		p.postfixExpression(f, n, t, exprAddrOf, flags)
+	case cc.PostfixExpressionPSelect: // PostfixExpression "->" IDENTIFIER
+		p.postfixExpression(f, n, t, exprAddrOf, flags)
+	case cc.PostfixExpressionInc: // PostfixExpression "++"
+		panic(todo("", p.pos(n)))
+	case cc.PostfixExpressionDec: // PostfixExpression "--"
+		panic(todo("", p.pos(n)))
+	case cc.PostfixExpressionComplit: // '(' TypeName ')' '{' InitializerList ',' '}'
+		panic(todo("", p.pos(n)))
+	case cc.PostfixExpressionTypeCmp: // "__builtin_types_compatible_p" '(' TypeName ',' TypeName ')'
+		panic(todo("", p.pos(n)))
+	case cc.PostfixExpressionChooseExpr:
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) postfixExpressionBool(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.PostfixExpressionPrimary: // PrimaryExpression
+		p.primaryExpression(f, n.PrimaryExpression, t, mode, flags)
+	case cc.PostfixExpressionIndex: // PostfixExpression '[' Expression ']'
+		p.w("(")
+		defer p.w(")")
+		defer p.w(" != 0")
+		p.postfixExpression(f, n, t, exprValue, flags)
+	case cc.PostfixExpressionCall: // PostfixExpression '(' ArgumentExpressionList ')'
+		p.postfixExpressionCall(f, n, t, mode, flags)
+	case cc.PostfixExpressionSelect: // PostfixExpression '.' IDENTIFIER
+		p.w("(")
+		defer p.w(")")
+		defer p.w(" != 0")
+		p.postfixExpression(f, n, t, exprValue, flags)
+	case cc.PostfixExpressionPSelect: // PostfixExpression "->" IDENTIFIER
+		p.w("(")
+		defer p.w(")")
+		defer p.w(" != 0")
+		p.postfixExpression(f, n, t, exprValue, flags)
+	case cc.PostfixExpressionInc: // PostfixExpression "++"
+		p.w("(")
+		defer p.w(")")
+		defer p.w(" != 0")
+		p.postfixExpression(f, n, t, exprValue, flags)
+	case cc.PostfixExpressionDec: // PostfixExpression "--"
+		p.w("(")
+		defer p.w(")")
+		defer p.w(" != 0")
+		p.postfixExpression(f, n, t, exprValue, flags)
+	case cc.PostfixExpressionComplit: // '(' TypeName ')' '{' InitializerList ',' '}'
+		panic(todo("", p.pos(n)))
+	case cc.PostfixExpressionTypeCmp: // "__builtin_types_compatible_p" '(' TypeName ',' TypeName ')'
+		panic(todo("", p.pos(n)))
+	case cc.PostfixExpressionChooseExpr:
+		p.w("(")
+		defer p.w(")")
+		defer p.w(" != 0")
+		p.postfixExpression(f, n, t, exprValue, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) postfixExpressionPSelect(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression "->" IDENTIFIER
+	switch n.Case {
+	case cc.PostfixExpressionPrimary: // PrimaryExpression
+		p.primaryExpression(f, n.PrimaryExpression, t, mode, flags)
+	case cc.PostfixExpressionIndex: // PostfixExpression '[' Expression ']'
+		p.postfixExpressionPSelectIndex(f, n, t, mode, flags)
+	case cc.PostfixExpressionCall: // PostfixExpression '(' ArgumentExpressionList ')'
+		p.postfixExpressionPSelectCall(f, n, t, mode, flags)
+	case cc.PostfixExpressionSelect: // PostfixExpression '.' IDENTIFIER
+		p.postfixExpressionPSelectSelect(f, n, t, mode, flags)
+	case cc.PostfixExpressionPSelect: // PostfixExpression "->" IDENTIFIER
+		p.postfixExpressionPSelectPSelect(f, n, t, mode, flags)
+	case cc.PostfixExpressionInc: // PostfixExpression "++"
+		panic(todo("", p.pos(n)))
+	case cc.PostfixExpressionDec: // PostfixExpression "--"
+		panic(todo("", p.pos(n)))
+	case cc.PostfixExpressionComplit: // '(' TypeName ')' '{' InitializerList ',' '}'
+		panic(todo("", p.pos(n)))
+	case cc.PostfixExpressionTypeCmp: // "__builtin_types_compatible_p" '(' TypeName ',' TypeName ')'
+		panic(todo("", p.pos(n)))
+	case cc.PostfixExpressionChooseExpr:
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) postfixExpressionPSelectSelect(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression '.' IDENTIFIER
+	switch k := p.opKind(f, n.PostfixExpression, n.PostfixExpression.Operand.Type()); k {
+	case opStruct:
+		p.postfixExpressionPSelectSelectStruct(f, n, t, mode, flags)
+	case opUnion:
+		p.postfixExpressionPSelectSelectUnion(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), k))
+	}
+}
+
+func (p *project) postfixExpressionPSelectSelectUnion(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression '.' IDENTIFIER
+	fld := n.Field
+	if fld.Offset() != 0 {
+		p.err(&n.Token2, "internal error, union field with non-zero offset: %s %v", n.Token2.Value, fld.Offset())
+	}
+	switch {
+	case n.Operand.Type().IsBitFieldType():
+		panic(todo("", p.pos(n)))
+	default:
+		if fld.IsBitField() {
+			p.err(&n.Token2, "internal error, wrong function for accessing a bit field: %s", n.Token2.Value)
+		}
+		pe := n.PostfixExpression.Operand.Type()
+		defer p.w("%s", p.convert(n, n.Operand, t, flags))
+		p.w("(*(**%s)(unsafe.Pointer(", p.typ(n, n.Operand.Type().Elem()))
+		p.postfixExpression(f, n.PostfixExpression, pe, exprAddrOf, flags)
+		p.w("/* .%s */", p.fieldName(n, n.Token2.Value))
+		p.w(")))")
+	}
+}
+
+func (p *project) postfixExpressionPSelectSelectStruct(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression '.' IDENTIFIER
+	fld := n.Field
+	switch {
+	case n.Operand.Type().IsBitFieldType():
+		panic(todo("", p.pos(n)))
+	default:
+		if fld.IsBitField() {
+			p.err(&n.Token2, "internal error, wrong function for accessing a bit field: %s", n.Token2.Value)
+		}
+		pe := n.PostfixExpression.Operand.Type()
+		p.w("(*%s)(unsafe.Pointer(", p.typ(n, t.Elem()))
+		p.postfixExpression(f, n.PostfixExpression, pe, exprSelect, flags)
+		p.w(".%s", p.fieldName(n, n.Token2.Value))
+		p.w("))")
+
+	}
+}
+
+func (p *project) postfixExpressionPSelectCall(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	p.w("(*%s)(unsafe.Pointer(", p.typ(n, n.Operand.Type().Elem()))
+	p.postfixExpressionCall(f, n, t, exprValue, flags)
+	p.w("))")
+}
+
+func (p *project) postfixExpressionPSelectIndex(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression '[' Expression ']'
+	switch k := p.opKind(f, n.PostfixExpression, n.PostfixExpression.Operand.Type()); k {
+	// case opArray:
+	// 	p.postfixExpressionSelectIndexArray(f, n, t, mode, flags)
+	case opNormal:
+		p.postfixExpressionPSelectIndexNormal(f, n, t, mode, flags)
+	case opArrayParameter:
+		p.postfixExpressionSelectIndexArrayParamater(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), k))
+	}
+}
+
+func (p *project) postfixExpressionPSelectIndexNormal(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	pe := n.PostfixExpression.Operand.Type()
+	// PostfixExpression '[' Expression ']'
+	switch {
+	case n.Operand.Type().IsBitFieldType():
+		panic(todo("", p.pos(n)))
+	case n.Operand.Type().Kind() == cc.Array:
+		panic(todo("", p.pos(n)))
+	case pe.Kind() == cc.Array:
+		p.w("(")
+		defer p.w(")")
+		p.w("(*(**%s)(unsafe.Pointer(", p.typ(n, n.Operand.Type().Elem()))
+		p.postfixExpression(f, n.PostfixExpression, pe, exprAddrOf, flags)
+		if !n.Expression.Operand.IsZero() {
+			p.nzUintptr(n, func() { p.expression(f, n.Expression, n.Expression.Operand.Type(), exprValue, flags) }, n.Expression.Operand)
+			if sz := pe.Decay().Elem().Size(); sz != 1 {
+				p.w("*%d", sz)
+			}
+		}
+		p.w(")))")
+	default:
+		p.w("(")
+		defer p.w(")")
+		p.w("(*(**%s)(unsafe.Pointer(", p.typ(n, n.Operand.Type().Elem()))
+		p.postfixExpression(f, n.PostfixExpression, pe, exprValue, flags)
+		if !n.Expression.Operand.IsZero() {
+			p.nzUintptr(n, func() { p.expression(f, n.Expression, n.Expression.Operand.Type(), exprValue, flags) }, n.Expression.Operand)
+			if sz := pe.Decay().Elem().Size(); sz != 1 {
+				p.w("*%d", sz)
+			}
+		}
+		p.w(")))")
+	}
+}
+
+func (p *project) postfixExpressionSelect(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression '.' IDENTIFIER
+	switch n.Case {
+	case cc.PostfixExpressionPrimary: // PrimaryExpression
+		p.primaryExpression(f, n.PrimaryExpression, t, mode, flags)
+	case cc.PostfixExpressionIndex: // PostfixExpression '[' Expression ']'
+		p.postfixExpressionSelectIndex(f, n, t, mode, flags)
+	case cc.PostfixExpressionCall: // PostfixExpression '(' ArgumentExpressionList ')'
+		p.postfixExpression(f, n, t, exprValue, flags)
+	case cc.PostfixExpressionSelect: // PostfixExpression '.' IDENTIFIER
+		p.postfixExpressionSelectSelect(f, n, t, mode, flags)
+	case cc.PostfixExpressionPSelect: // PostfixExpression "->" IDENTIFIER
+		p.postfixExpressionSelectPSelect(f, n, t, mode, flags)
+	case cc.PostfixExpressionInc: // PostfixExpression "++"
+		panic(todo("", p.pos(n)))
+	case cc.PostfixExpressionDec: // PostfixExpression "--"
+		panic(todo("", p.pos(n)))
+	case cc.PostfixExpressionComplit: // '(' TypeName ')' '{' InitializerList ',' '}'
+		panic(todo("", p.pos(n)))
+	case cc.PostfixExpressionTypeCmp: // "__builtin_types_compatible_p" '(' TypeName ',' TypeName ')'
+		panic(todo("", p.pos(n)))
+	case cc.PostfixExpressionChooseExpr:
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) postfixExpressionPSelectPSelect(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression "->" IDENTIFIER
+	switch k := p.opKind(f, n.PostfixExpression, n.PostfixExpression.Operand.Type().Elem()); k {
+	case opStruct:
+		p.postfixExpressionPSelectPSelectStruct(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), k))
+	}
+}
+
+func (p *project) postfixExpressionPSelectPSelectStruct(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression "->" IDENTIFIER
+	fld := n.Field
+	switch {
+	case n.Operand.Type().IsBitFieldType():
+		panic(todo("", p.pos(n)))
+	default:
+		if fld.IsBitField() {
+			p.err(&n.Token2, "internal error, wrong function for accessing a bit field: %s", n.Token2.Value)
+		}
+		pe := n.PostfixExpression.Operand.Type()
+		defer p.w("%s", p.convert(n, n.Operand, t, flags))
+		p.w("(*%s)(unsafe.Pointer(", p.typ(n, t.Elem()))
+		p.postfixExpression(f, n.PostfixExpression, pe, exprPSelect, flags)
+		p.w(".%s", p.fieldName(n, n.Token2.Value))
+		p.w("))")
+	}
+}
+
+func (p *project) postfixExpressionSelectPSelect(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression "->" IDENTIFIER
+	switch k := p.opKind(f, n.PostfixExpression, n.PostfixExpression.Operand.Type().Elem()); k {
+	case opStruct:
+		p.postfixExpressionSelectPSelectStruct(f, n, t, mode, flags)
+	case opUnion:
+		p.postfixExpressionSelectPSelectUnion(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), k))
+	}
+}
+
+func (p *project) postfixExpressionSelectPSelectUnion(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression "->" IDENTIFIER
+	fld := n.Field
+	if fld.Offset() != 0 {
+		p.err(&n.Token2, "internal error, union field with non-zero offset: %s %v", n.Token2.Value, fld.Offset())
+	}
+	switch {
+	case n.Operand.Type().IsBitFieldType():
+		panic(todo("", p.pos(n)))
+	case n.Operand.Type().Kind() == cc.Array:
+		panic(todo("", p.pos(n)))
+	default:
+		if fld.IsBitField() {
+			p.err(&n.Token2, "internal error, wrong function for accessing a bit field: %s", n.Token2.Value)
+		}
+		pe := n.PostfixExpression.Operand.Type()
+		defer p.w("%s", p.convert(n, n.Operand, t, flags))
+		p.w("(*%s)(unsafe.Pointer(", p.typ(n, n.Operand.Type()))
+		p.postfixExpression(f, n.PostfixExpression, pe, exprValue, flags)
+		p.w("))")
+	}
+}
+
+func (p *project) postfixExpressionSelectPSelectStruct(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression "->" IDENTIFIER
+	fld := n.Field
+	switch {
+	case n.Operand.Type().IsBitFieldType():
+		panic(todo("", p.pos(n)))
+	default:
+		if fld.IsBitField() {
+			p.err(&n.Token2, "internal error, wrong function for accessing a bit field: %s", n.Token2.Value)
+		}
+		pe := n.PostfixExpression.Operand.Type()
+		defer p.w("%s", p.convert(n, n.Operand, t, flags))
+		et := n.PostfixExpression.Operand.Type().Elem()
+		fld, path, ok := et.FieldByName2(n.Token2.Value)
+		switch {
+		case !ok:
+			panic(todo("", n.Token.Position()))
+		case fld.InUnion():
+			p.w("(*(*%s)(unsafe.Pointer(", p.typ(n, n.Operand.Type()))
+			p.postfixExpression(f, n.PostfixExpression, pe, exprValue, flags)
+			p.w("%s)))", nonZeroUintptr(pathOff(et, path)))
+		case len(path) != 1:
+			panic(todo("", n.Token.Position()))
+		default:
+			p.w("(*%s)(unsafe.Pointer(", p.typ(n, pe.Elem()))
+			switch {
+			case pe.Kind() == cc.Array:
+				p.postfixExpression(f, n.PostfixExpression, pe, exprAddrOf, flags)
+			default:
+				p.postfixExpression(f, n.PostfixExpression, pe, exprValue, flags)
+			}
+			p.w(")).%s", p.fieldName(n, n.Token2.Value))
+		}
+	}
+}
+
+func (p *project) postfixExpressionSelectSelect(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression '.' IDENTIFIER
+	switch k := p.opKind(f, n.PostfixExpression, n.PostfixExpression.Operand.Type()); k {
+	case opUnion:
+		p.postfixExpressionSelectSelectUnion(f, n, t, mode, flags)
+	case opStruct:
+		p.postfixExpressionSelectSelectStruct(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), k))
+	}
+}
+
+func (p *project) postfixExpressionSelectSelectStruct(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression '.' IDENTIFIER
+	fld := n.Field
+	switch {
+	case n.Operand.Type().IsBitFieldType():
+		panic(todo("", p.pos(n)))
+	case n.Operand.Type().Kind() == cc.Array:
+		panic(todo("", p.pos(n)))
+	default:
+		if fld.IsBitField() {
+			p.err(&n.Token2, "internal error, wrong function for accessing a bit field: %s", n.Token2.Value)
+		}
+		pe := n.PostfixExpression.Operand.Type()
+		p.postfixExpression(f, n.PostfixExpression, pe, exprSelect, flags)
+		p.w(".%s", p.fieldName(n, n.Token2.Value))
+	}
+}
+
+func (p *project) postfixExpressionSelectSelectUnion(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression '.' IDENTIFIER
+	fld := n.Field
+	if fld.Offset() != 0 {
+		p.err(&n.Token2, "internal error, union field with non-zero offset: %s %v", n.Token2.Value, fld.Offset())
+	}
+	switch {
+	case n.Operand.Type().IsBitFieldType():
+		panic(todo("", p.pos(n)))
+	case n.Operand.Type().Kind() == cc.Array:
+		panic(todo("", p.pos(n)))
+	default:
+		if fld.IsBitField() {
+			p.err(&n.Token2, "internal error, wrong function for accessing a bit field: %s", n.Token2.Value)
+		}
+		pe := n.PostfixExpression.Operand.Type()
+		defer p.w("%s", p.convert(n, n.Operand, t, flags))
+		p.w("(*%s)(unsafe.Pointer(", p.typ(n, n.Operand.Type()))
+		p.postfixExpression(f, n.PostfixExpression, pe, exprAddrOf, flags)
+		p.w("))")
+	}
+}
+
+func (p *project) postfixExpressionSelectIndex(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression '[' Expression ']'
+	switch k := p.opKind(f, n.PostfixExpression, n.PostfixExpression.Operand.Type()); k {
+	case opArray:
+		p.postfixExpressionSelectIndexArray(f, n, t, mode, flags)
+	case opNormal:
+		p.postfixExpressionSelectIndexNormal(f, n, t, mode, flags)
+	case opArrayParameter:
+		p.postfixExpressionSelectIndexArrayParamater(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), k))
+	}
+}
+
+func (p *project) postfixExpressionSelectIndexArrayParamater(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	pe := n.PostfixExpression.Operand.Type()
+	// PostfixExpression '[' Expression ']'
+	switch {
+	case n.Operand.Type().IsBitFieldType():
+		panic(todo("", p.pos(n)))
+	case n.Operand.Type().Kind() == cc.Array:
+		panic(todo("", p.pos(n)))
+	default:
+		p.w("(*%s)(unsafe.Pointer(", p.typ(n, pe.Elem()))
+		p.postfixExpression(f, n.PostfixExpression, pe, exprValue, flags)
+		if !n.Expression.Operand.IsZero() {
+			p.nzUintptr(n, func() { p.expression(f, n.Expression, n.Expression.Operand.Type(), exprValue, flags) }, n.Expression.Operand)
+			if sz := pe.Decay().Elem().Size(); sz != 1 {
+				p.w("*%d", sz)
+			}
+		}
+		p.w("))")
+	}
+}
+
+func (p *project) postfixExpressionSelectIndexNormal(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	pe := n.PostfixExpression.Operand.Type()
+	// PostfixExpression '[' Expression ']'
+	switch {
+	case n.Operand.Type().IsBitFieldType():
+		panic(todo("", p.pos(n)))
+	case n.Operand.Type().Kind() == cc.Array:
+		panic(todo("", p.pos(n)))
+	case pe.Kind() != cc.Ptr:
+		p.w("(*%s)(unsafe.Pointer(", p.typ(n, pe.Elem()))
+		p.postfixExpression(f, n.PostfixExpression, pe, exprAddrOf, flags)
+		if !n.Expression.Operand.IsZero() {
+			p.nzUintptr(n, func() { p.expression(f, n.Expression, n.Expression.Operand.Type(), exprValue, flags) }, n.Expression.Operand)
+			if sz := pe.Decay().Elem().Size(); sz != 1 {
+				p.w("*%d", sz)
+			}
+		}
+		p.w("))")
+	default:
+		p.w("(*%s)(unsafe.Pointer(", p.typ(n, pe.Elem()))
+		p.postfixExpression(f, n.PostfixExpression, pe, exprValue, flags)
+		if !n.Expression.Operand.IsZero() {
+			p.nzUintptr(n, func() { p.expression(f, n.Expression, n.Expression.Operand.Type(), exprValue, flags) }, n.Expression.Operand)
+			if sz := pe.Decay().Elem().Size(); sz != 1 {
+				p.w("*%d", sz)
+			}
+		}
+		p.w("))")
+	}
+}
+
+func (p *project) postfixExpressionSelectIndexArray(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression '[' Expression ']'
+	switch {
+	case n.Operand.Type().IsBitFieldType():
+		panic(todo("", p.pos(n)))
+	case n.Operand.Type().Kind() == cc.Array:
+		panic(todo("", p.pos(n)))
+	default:
+		pe := n.PostfixExpression.Operand.Type()
+		p.postfixExpression(f, n.PostfixExpression, pe, mode, flags)
+		p.w("[")
+		p.expression(f, n.Expression, n.Expression.Operand.Type(), exprValue, flags)
+		p.w("]")
+	}
+}
+
+func (p *project) postfixExpressionAddrOf(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.PostfixExpressionPrimary: // PrimaryExpression
+		p.primaryExpression(f, n.PrimaryExpression, t, mode, flags)
+	case cc.PostfixExpressionIndex: // PostfixExpression '[' Expression ']'
+		p.postfixExpressionAddrOfIndex(f, n, t, mode, flags)
+	case cc.PostfixExpressionCall: // PostfixExpression '(' ArgumentExpressionList ')'
+		ot := n.Operand.Type()
+		switch ot.Kind() {
+		case cc.Struct, cc.Union:
+			// ok
+		default:
+			p.err(n, "cannot take address of value of type %v", n.Operand.Type())
+			return
+		}
+
+		if p.pass1 {
+			off := roundup(f.off, uintptr(ot.Align()))
+			f.complits[n] = off
+			f.off += ot.Size()
+			return
+		}
+
+		off := f.complits[n]
+		p.w("func() uintptr { *(*%s)(unsafe.Pointer(%s%s)) = ", p.typ(n, ot), f.bpName, nonZeroUintptr(off))
+		p.postfixExpressionValue(f, n, ot, exprValue, flags)
+		p.w("; return %s%s }()", f.bpName, nonZeroUintptr(off))
+	case cc.PostfixExpressionSelect: // PostfixExpression '.' IDENTIFIER
+		p.postfixExpressionAddrOfSelect(f, n, t, mode, flags)
+	case cc.PostfixExpressionPSelect: // PostfixExpression "->" IDENTIFIER
+		p.postfixExpressionAddrOfPSelect(f, n, t, mode, flags)
+	case cc.PostfixExpressionInc: // PostfixExpression "++"
+		p.postfixExpressionIncDec(f, n, "++", "+=", t, exprLValue, flags)
+	case cc.PostfixExpressionDec: // PostfixExpression "--"
+		panic(todo("", p.pos(n)))
+	case cc.PostfixExpressionComplit: // '(' TypeName ')' '{' InitializerList ',' '}'
+		tn := n.TypeName.Type()
+		switch tn.Decay().Kind() {
+		case cc.Ptr:
+			switch tn.Kind() {
+			case cc.Array:
+				switch {
+				case p.pass1:
+					off := roundup(f.off, uintptr(tn.Elem().Align()))
+					f.complits[n] = off
+					f.off += tn.Size()
+				default:
+					off := f.complits[n]
+					p.w(" func() uintptr { *(*%s)(unsafe.Pointer(%s%s)) = ", p.typ(n, tn), f.bpName, nonZeroUintptr(off))
+					p.initializer(f, &cc.Initializer{Case: cc.InitializerInitList, InitializerList: n.InitializerList}, tn, cc.Automatic, nil)
+					p.w("; return %s%s }()", f.bpName, nonZeroUintptr(off))
+				}
+			default:
+				panic(todo("%v: %v", n.Position(), tn))
+			}
+		default:
+			switch {
+			case p.pass1:
+				off := roundup(f.off, uintptr(tn.Align()))
+				f.complits[n] = off
+				f.off += tn.Size()
+			default:
+				off := f.complits[n]
+				p.w(" func() uintptr { *(*%s)(unsafe.Pointer(%s%s)) = ", p.typ(n, tn), f.bpName, nonZeroUintptr(off))
+				p.initializer(f, &cc.Initializer{Case: cc.InitializerInitList, InitializerList: n.InitializerList}, tn, cc.Automatic, nil)
+				p.w("; return %s%s }()", f.bpName, nonZeroUintptr(off))
+			}
+		}
+	case cc.PostfixExpressionTypeCmp: // "__builtin_types_compatible_p" '(' TypeName ',' TypeName ')'
+		panic(todo("", p.pos(n)))
+	case cc.PostfixExpressionChooseExpr:
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) postfixExpressionAddrOfPSelect(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression "->" IDENTIFIER
+	p.w("(")
+	defer p.w(")")
+	pe := n.PostfixExpression.Operand.Type()
+	switch {
+	case n.Operand.Type().IsBitFieldType():
+		p.postfixExpression(f, n.PostfixExpression, pe, exprValue, flags)
+		p.bitFldOff(pe.Elem(), n.Token2)
+	case pe.Kind() == cc.Array:
+		p.postfixExpression(f, n.PostfixExpression, pe, exprDecay, flags)
+		p.fldOff(pe.Elem(), n.Token2)
+	default:
+		p.postfixExpression(f, n.PostfixExpression, pe, exprValue, flags)
+		p.fldOff(pe.Elem(), n.Token2)
+	}
+}
+
+func (p *project) postfixExpressionAddrOfIndex(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression '[' Expression ']'
+	p.w("(")
+	defer p.w(")")
+	switch {
+	case n.Operand.Type().Kind() == cc.Array:
+		fallthrough
+	default:
+		pe := n.PostfixExpression.Operand.Type()
+		d := n.PostfixExpression.Declarator()
+		switch {
+		case pe.Kind() == cc.Ptr:
+			p.postfixExpression(f, n.PostfixExpression, pe, exprValue, flags)
+		case pe.Kind() == cc.Array && d != nil && d.IsParameter:
+			p.postfixExpression(f, n.PostfixExpression, pe, exprValue, flags)
+		default:
+			p.postfixExpression(f, n.PostfixExpression, pe, mode, flags)
+		}
+		if !n.Expression.Operand.IsZero() {
+			p.nzUintptr(n, func() { p.expression(f, n.Expression, n.Expression.Operand.Type(), exprValue, flags) }, n.Expression.Operand)
+			if sz := pe.Decay().Elem().Size(); sz != 1 {
+				p.w("*%d", sz)
+			}
+		}
+	}
+}
+
+func (p *project) postfixExpressionAddrOfSelect(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression '.' IDENTIFIER
+	p.w("(")
+	defer p.w(")")
+	switch {
+	case n.Operand.Type().IsBitFieldType():
+		pe := n.PostfixExpression.Operand.Type()
+		p.postfixExpression(f, n.PostfixExpression, nil, mode, flags)
+		p.bitFldOff(pe, n.Token2)
+	case n.Operand.Type().Kind() == cc.Array:
+		fallthrough
+	default:
+		pe := n.PostfixExpression.Operand.Type()
+		p.postfixExpression(f, n.PostfixExpression, nil, mode, flags)
+		p.fldOff(pe, n.Token2)
+	}
+}
+
+func (p *project) postfixExpressionFunc(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.PostfixExpressionPrimary: // PrimaryExpression
+		p.primaryExpression(f, n.PrimaryExpression, t, mode, flags)
+	case cc.PostfixExpressionIndex: // PostfixExpression '[' Expression ']'
+		switch n.Operand.Type().Kind() {
+		case cc.Ptr:
+			switch et := n.Operand.Type().Elem(); et.Kind() {
+			case cc.Function:
+				p.fnVal(n, f, func() {
+					p.postfixExpression(f, n, n.Operand.Type(), exprValue, flags)
+				}, nil, n.Operand.Type(), 0, mode, flags)
+			default:
+				panic(todo("", p.pos(n), et, et.Kind()))
+			}
+		default:
+			panic(todo("", n.Position(), n.Operand.Type()))
+		}
+	case cc.PostfixExpressionCall: // PostfixExpression '(' ArgumentExpressionList ')'
+		switch n.Operand.Type().Kind() {
+		case cc.Ptr:
+			switch et := n.Operand.Type().Elem(); et.Kind() {
+			case cc.Function:
+				p.fnVal(n, f, func() {
+					p.postfixExpressionCall(f, n, t, exprValue, flags)
+				}, nil, n.Operand.Type(), 0, mode, flags)
+			default:
+				panic(todo("", p.pos(n), et, et.Kind()))
+			}
+		default:
+			panic(todo("", n.Position(), n.Operand.Type()))
+		}
+	case cc.PostfixExpressionSelect: // PostfixExpression '.' IDENTIFIER
+		switch n.Operand.Type().Kind() {
+		case cc.Ptr:
+			switch n.Operand.Type().Kind() {
+			case cc.Ptr:
+				switch et := n.Operand.Type().Elem(); et.Kind() {
+				case cc.Function:
+					p.fnVal(n, f, func() { p.postfixExpression(f, n, p.ptrType, exprValue, flags) }, nil, n.Operand.Type(), 0, mode, flags)
+				default:
+					panic(todo("", p.pos(n), et, et.Kind()))
+				}
+			default:
+				panic(todo("", p.pos(n), n.Operand.Type(), n.Operand.Type().Kind()))
+			}
+		default:
+			panic(todo("", n.Position(), n.Operand.Type()))
+		}
+	case cc.PostfixExpressionPSelect: // PostfixExpression "->" IDENTIFIER
+		p.fnVal(n, f, func() { p.postfixExpression(f, n, p.ptrType, exprValue, flags) }, nil, n.Operand.Type(), 0, mode, flags)
+	case cc.PostfixExpressionInc: // PostfixExpression "++"
+		panic(todo("", p.pos(n)))
+	case cc.PostfixExpressionDec: // PostfixExpression "--"
+		panic(todo("", p.pos(n)))
+	case cc.PostfixExpressionComplit: // '(' TypeName ')' '{' InitializerList ',' '}'
+		panic(todo("", p.pos(n)))
+	case cc.PostfixExpressionTypeCmp: // "__builtin_types_compatible_p" '(' TypeName ',' TypeName ')'
+		panic(todo("", p.pos(n)))
+	case cc.PostfixExpressionChooseExpr:
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) postfixExpressionVoid(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.PostfixExpressionPrimary: // PrimaryExpression
+		p.primaryExpression(f, n.PrimaryExpression, t, mode, flags)
+	case cc.PostfixExpressionIndex: // PostfixExpression '[' Expression ']'
+		p.w("_ = ")
+		p.postfixExpression(f, n, n.Operand.Type(), exprValue, flags)
+	case cc.PostfixExpressionCall: // PostfixExpression '(' ArgumentExpressionList ')'
+		p.postfixExpressionCall(f, n, n.Operand.Type(), mode, flags)
+	case cc.PostfixExpressionSelect: // PostfixExpression '.' IDENTIFIER
+		p.w("_ = ")
+		p.postfixExpression(f, n, n.Operand.Type(), exprValue, flags)
+	case cc.PostfixExpressionPSelect: // PostfixExpression "->" IDENTIFIER
+		p.w("_ = ")
+		p.postfixExpression(f, n, n.Operand.Type(), exprValue, flags)
+	case cc.PostfixExpressionInc: // PostfixExpression "++"
+		p.postfixExpressionIncDec(f, n, "++", "+=", t, mode, flags)
+	case cc.PostfixExpressionDec: // PostfixExpression "--"
+		p.postfixExpressionIncDec(f, n, "--", "-=", t, mode, flags)
+	case cc.PostfixExpressionComplit: // '(' TypeName ')' '{' InitializerList ',' '}'
+		tn := n.TypeName.Type()
+		switch tn.Decay().Kind() {
+		case cc.Ptr:
+			switch tn.Kind() {
+			case cc.Array:
+				switch {
+				case p.pass1:
+					off := roundup(f.off, uintptr(tn.Elem().Align()))
+					f.complits[n] = off
+					f.off += tn.Size()
+				default:
+					off := f.complits[n]
+					p.w("*(*%s)(unsafe.Pointer(%s%s)) = ", p.typ(n, tn), f.bpName, nonZeroUintptr(off))
+					p.initializer(f, &cc.Initializer{Case: cc.InitializerInitList, InitializerList: n.InitializerList}, tn, cc.Automatic, nil)
+				}
+				return
+			default:
+				panic(todo("%v: %v", n.Position(), tn))
+			}
+		}
+
+		defer p.w("%s", p.convertType(n, tn, t, flags))
+		p.w("_ = ")
+		p.initializer(f, &cc.Initializer{Case: cc.InitializerInitList, InitializerList: n.InitializerList}, tn, cc.Automatic, nil)
+	case cc.PostfixExpressionTypeCmp: // "__builtin_types_compatible_p" '(' TypeName ',' TypeName ')'
+		panic(todo("", p.pos(n)))
+	case cc.PostfixExpressionChooseExpr:
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) postfixExpressionValue(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.PostfixExpressionPrimary: // PrimaryExpression
+		if p.isArray(f, n.PrimaryExpression, n.Operand.Type()) && t.Kind() == cc.Ptr {
+			mode = exprDecay
+		}
+		p.primaryExpression(f, n.PrimaryExpression, t, mode, flags)
+	case cc.PostfixExpressionIndex: // PostfixExpression '[' Expression ']'
+		p.postfixExpressionValueIndex(f, n, t, mode, flags)
+	case cc.PostfixExpressionCall: // PostfixExpression '(' ArgumentExpressionList ')'
+		p.postfixExpressionCall(f, n, t, mode, flags)
+	case cc.PostfixExpressionSelect: // PostfixExpression '.' IDENTIFIER
+		p.postfixExpressionValueSelect(f, n, t, mode, flags)
+	case cc.PostfixExpressionPSelect: // PostfixExpression "->" IDENTIFIER
+		p.postfixExpressionValuePSelect(f, n, t, mode, flags)
+	case cc.PostfixExpressionInc: // PostfixExpression "++"
+		p.postfixExpressionIncDec(f, n, "++", "+=", t, mode, flags)
+	case cc.PostfixExpressionDec: // PostfixExpression "--"
+		p.postfixExpressionIncDec(f, n, "--", "-=", t, mode, flags)
+	case cc.PostfixExpressionComplit: // '(' TypeName ')' '{' InitializerList ',' '}'
+		tn := n.TypeName.Type()
+		switch tn.Decay().Kind() {
+		case cc.Ptr:
+			switch tn.Kind() {
+			case cc.Array:
+				switch {
+				case p.pass1:
+					off := roundup(f.off, uintptr(tn.Elem().Align()))
+					f.complits[n] = off
+					f.off += tn.Size()
+				default:
+					off := f.complits[n]
+					p.w(" func() uintptr { *(*%s)(unsafe.Pointer(%s%s)) = ", p.typ(n, tn), f.bpName, nonZeroUintptr(off))
+					p.initializer(f, &cc.Initializer{Case: cc.InitializerInitList, InitializerList: n.InitializerList}, tn, cc.Automatic, nil)
+					p.w("; return %s%s }()", f.bpName, nonZeroUintptr(off))
+				}
+				return
+			default:
+				panic(todo("%v: %v", n.Position(), tn))
+			}
+		}
+
+		defer p.w("%s", p.convertType(n, tn, t, flags))
+		p.initializer(f, &cc.Initializer{Case: cc.InitializerInitList, InitializerList: n.InitializerList}, tn, cc.Automatic, nil)
+	case cc.PostfixExpressionTypeCmp: // "__builtin_types_compatible_p" '(' TypeName ',' TypeName ')'
+		// Built-in Function: int __builtin_types_compatible_p (type1, type2) You can
+		// use the built-in function __builtin_types_compatible_p to determine whether
+		// two types are the same.
+		//
+		// This built-in function returns 1 if the unqualified versions of the types
+		// type1 and type2 (which are types, not expressions) are compatible, 0
+		// otherwise. The result of this built-in function can be used in integer
+		// constant expressions.
+		//
+		// This built-in function ignores top level qualifiers (e.g., const, volatile).
+		// For example, int is equivalent to const int.
+		//
+		// The type int[] and int[5] are compatible. On the other hand, int and char *
+		// are not compatible, even if the size of their types, on the particular
+		// architecture are the same. Also, the amount of pointer indirection is taken
+		// into account when determining similarity. Consequently, short * is not
+		// similar to short **. Furthermore, two types that are typedefed are
+		// considered compatible if their underlying types are compatible.
+		//
+		// An enum type is not considered to be compatible with another enum type even
+		// if both are compatible with the same integer type; this is what the C
+		// standard specifies. For example, enum {foo, bar} is not similar to enum
+		// {hot, dog}.
+		//
+		// You typically use this function in code whose execution varies depending on
+		// the arguments’ types. For example:
+		//
+		//	#define foo(x)                                                  \
+		//	  ({                                                           \
+		//	    typeof (x) tmp = (x);                                       \
+		//	    if (__builtin_types_compatible_p (typeof (x), long double)) \
+		//	      tmp = foo_long_double (tmp);                              \
+		//	    else if (__builtin_types_compatible_p (typeof (x), double)) \
+		//	      tmp = foo_double (tmp);                                   \
+		//	    else if (__builtin_types_compatible_p (typeof (x), float))  \
+		//	      tmp = foo_float (tmp);                                    \
+		//	    else                                                        \
+		//	      abort ();                                                 \
+		//	    tmp;                                                        \
+		//	  })
+		//
+		// Note: This construct is only available for C.
+		p.w(" %d ", n.Operand.Value())
+	case cc.PostfixExpressionChooseExpr: // "__builtin_choose_expr" '(' AssignmentExpression ',' AssignmentExpression ',' AssignmentExpression ')'
+		// You can use the built-in function __builtin_choose_expr to evaluate code
+		// depending on the value of a constant expression. This built-in function
+		// returns exp1 if const_exp, which is an integer constant expression, is
+		// nonzero. Otherwise it returns exp2.
+		//
+		// This built-in function is analogous to the ‘? :’ operator in C, except that
+		// the expression returned has its type unaltered by promotion rules. Also, the
+		// built-in function does not evaluate the expression that is not chosen. For
+		// example, if const_exp evaluates to true, exp2 is not evaluated even if it
+		// has side effects.
+		//
+		// This built-in function can return an lvalue if the chosen argument is an
+		// lvalue.
+		//
+		// If exp1 is returned, the return type is the same as exp1’s type. Similarly,
+		// if exp2 is returned, its return type is the same as exp2.
+		//
+		// Example:
+		//
+		// 	#define foo(x)                                                \
+		// 	  __builtin_choose_expr (                                     \
+		// 	    __builtin_types_compatible_p (typeof (x), double),        \
+		// 	    foo_double (x),                                           \
+		// 	    __builtin_choose_expr (                                   \
+		// 	      __builtin_types_compatible_p (typeof (x), float),       \
+		// 	      foo_float (x),                                          \
+		// 	      /* The void expression results in a compile-time error  \
+		// 	         when assigning the result to something.  */          \
+		// 	      (void)0))
+		//
+		// Note: This construct is only available for C. Furthermore, the unused
+		// expression (exp1 or exp2 depending on the value of const_exp) may still
+		// generate syntax errors. This may change in future revisions.
+		switch op := n.AssignmentExpression.Operand; {
+		case op.IsNonZero():
+			p.assignmentExpression(f, n.AssignmentExpression2, t, mode, flags)
+		case op.IsZero():
+			p.assignmentExpression(f, n.AssignmentExpression3, t, mode, flags)
+		default:
+			panic(todo(""))
+		}
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) postfixExpressionValuePSelect(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression "->" IDENTIFIER
+	switch k := p.opKind(f, n.PostfixExpression, n.PostfixExpression.Operand.Type().Elem()); k {
+	case opStruct:
+		p.postfixExpressionValuePSelectStruct(f, n, t, mode, flags)
+	case opUnion:
+		p.postfixExpressionValuePSelectUnion(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), k))
+	}
+}
+
+func (p *project) postfixExpressionValuePSelectUnion(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression "->" IDENTIFIER
+	fld := n.Field
+	if fld.Offset() != 0 {
+		p.err(&n.Token2, "internal error, union field with non-zero offset: %s %v", n.Token2.Value, fld.Offset())
+	}
+	pe := n.PostfixExpression.Operand.Type()
+	switch {
+	case n.Operand.Type().IsBitFieldType():
+		panic(todo("", p.pos(n)))
+	case n.Operand.Type().Kind() == cc.Array:
+		panic(todo("", p.pos(n)))
+	default:
+		if fld.IsBitField() {
+			p.err(&n.Token2, "internal error, wrong function for accessing a bit field: %s", n.Token2.Value)
+		}
+		defer p.w("%s", p.convert(n, n.Operand, t, flags))
+		p.w("*(*%s)(unsafe.Pointer(", p.typ(n, n.Operand.Type()))
+		p.postfixExpression(f, n.PostfixExpression, pe, exprValue, flags)
+		p.w("/* .%s */", p.fieldName(n, n.Token2.Value))
+		p.w("))")
+	}
+}
+
+func (p *project) postfixExpressionValuePSelectStruct(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression "->" IDENTIFIER
+	fld := n.Field
+	pe := n.PostfixExpression.Operand.Type()
+	k := p.opKind(f, n.PostfixExpression, n.PostfixExpression.Operand.Type())
+	switch {
+	case n.Operand.Type().IsBitFieldType():
+		p.w("(")
+		defer p.w(")")
+		fld := n.Field
+		defer p.w("%s", p.convertType(n, fld.Promote(), t, flags))
+		switch pe.Kind() {
+		case cc.Array:
+			x := p.convertType(n, nil, fld.Promote(), flags)
+			p.w("*(*uint%d)(unsafe.Pointer(", fld.BitFieldBlockWidth())
+			p.postfixExpression(f, n.PostfixExpression, pe, exprDecay, flags)
+			p.bitFldOff(pe.Elem(), n.Token2)
+			p.w("))")
+			p.w("&%#x>>%d%s", fld.Mask(), fld.BitFieldOffset(), x)
+			if fld.Type().IsSignedType() {
+				panic(todo(""))
+				p.w("<<%d>>%[1]d", int(fld.Promote().Size()*8)-fld.BitFieldWidth())
+			}
+		default:
+			x := p.convertType(n, nil, fld.Promote(), flags)
+			p.w("*(*uint%d)(unsafe.Pointer(", fld.BitFieldBlockWidth())
+			p.postfixExpression(f, n.PostfixExpression, pe, exprValue, flags)
+			p.bitFldOff(pe.Elem(), n.Token2)
+			p.w("))&%#x>>%d%s", fld.Mask(), fld.BitFieldOffset(), x)
+			if fld.Type().IsSignedType() {
+				p.w("<<%d>>%[1]d", int(fld.Promote().Size()*8)-fld.BitFieldWidth())
+			}
+		}
+	case n.Operand.Type().Kind() == cc.Array:
+		defer p.w("%s", p.convertType(n, n.Operand.Type().Decay(), t.Decay(), flags))
+		p.postfixExpression(f, n.PostfixExpression, pe, exprValue, flags)
+		p.fldOff(n.PostfixExpression.Operand.Type().Elem(), n.Token2)
+	case k == opArray:
+		defer p.w("%s", p.convert(n, n.Operand, t, flags))
+		p.postfixExpression(f, n.PostfixExpression, pe, exprValue, flags)
+		p.w("[0].%s", p.fieldName(n, n.Token2.Value))
+	default:
+		if fld.IsBitField() {
+			p.err(&n.Token2, "internal error, wrong function for accessing a bit field: %s", n.Token2.Value)
+		}
+		defer p.w("%s", p.convert(n, n.Operand, t, flags))
+		et := pe.Elem()
+		fld, path, ok := et.FieldByName2(n.Token2.Value)
+		switch {
+		case !ok:
+			panic(todo(""))
+		case fld.InUnion():
+			p.w("*(*%s)(unsafe.Pointer(", p.typ(n, n.Operand.Type()))
+			p.postfixExpression(f, n.PostfixExpression, pe, exprValue, flags)
+			p.w("%s))", nonZeroUintptr(pathOff(et, path)))
+		case len(path) != 1:
+			panic(todo(""))
+		default:
+			p.w("(*%s)(unsafe.Pointer(", p.typ(n, pe.Elem()))
+			p.postfixExpression(f, n.PostfixExpression, pe, exprValue, flags)
+			p.w(")).%s", p.fieldName(n, n.Token2.Value))
+		}
+	}
+}
+
+func pathOff(t cc.Type, path []int) (r uintptr) {
+	for len(path) != 0 {
+		f := t.FieldByIndex(path[:1])
+		r += f.Offset()
+		path = path[1:]
+		t = f.Type()
+	}
+	return r
+}
+
+func (p *project) postfixExpressionValueIndex(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression '[' Expression ']'
+	switch k := p.opKind(f, n.PostfixExpression, n.PostfixExpression.Operand.Type()); k {
+	case opArray:
+		p.postfixExpressionValueIndexArray(f, n, t, mode, flags)
+	case opNormal:
+		p.postfixExpressionValueIndexNormal(f, n, t, mode, flags)
+	case opArrayParameter:
+		p.postfixExpressionValueIndexArrayParameter(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), k))
+	}
+}
+func (p *project) postfixExpressionValueIndexArrayParameter(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression '[' Expression ']'
+	pe := n.PostfixExpression.Operand.Type()
+	switch {
+	case n.Operand.Type().Kind() == cc.Array:
+		defer p.w("%s", p.convert(n, n.Operand, t, flags))
+		p.w("(")
+		defer p.w(")")
+		p.postfixExpression(f, n.PostfixExpression, pe, exprValue, flags)
+		if !n.Expression.Operand.IsZero() {
+			p.nzUintptr(n, func() { p.expression(f, n.Expression, n.Expression.Operand.Type(), exprValue, flags) }, n.Expression.Operand)
+			if sz := pe.Elem().Size(); sz != 1 {
+				p.w("*%d", sz)
+			}
+		}
+	default:
+		defer p.w("%s", p.convert(n, n.Operand, t, flags))
+		p.w("*(*%s)(unsafe.Pointer(", p.typ(n, pe.Elem()))
+		p.postfixExpression(f, n.PostfixExpression, pe, exprValue, flags)
+		if !n.Expression.Operand.IsZero() {
+			p.nzUintptr(n, func() { p.expression(f, n.Expression, n.Expression.Operand.Type(), exprValue, flags) }, n.Expression.Operand)
+			if sz := pe.Elem().Size(); sz != 1 {
+				p.w("*%d", sz)
+			}
+		}
+		p.w("))")
+	}
+}
+
+func (p *project) postfixExpressionValueIndexNormal(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression '[' Expression ']'
+	switch {
+	case n.Operand.Type().Kind() == cc.Array:
+		p.w("(")
+		defer p.w(")")
+		pe := n.PostfixExpression.Operand.Type()
+		defer p.w("%s", p.convert(n, n.Operand, t, flags))
+		p.postfixExpression(f, n.PostfixExpression, pe, exprValue, flags)
+		if !n.Expression.Operand.IsZero() {
+			p.nzUintptr(n, func() { p.expression(f, n.Expression, n.Expression.Operand.Type(), exprValue, flags) }, n.Expression.Operand)
+			if sz := pe.Elem().Size(); sz != 1 {
+				p.w("*%d", sz)
+			}
+		}
+	default:
+		switch pe := n.PostfixExpression.Operand.Type(); pe.Kind() {
+		case cc.Ptr:
+			defer p.w("%s", p.convert(n, n.Operand, t, flags))
+			p.w("*(*%s)(unsafe.Pointer(", p.typ(n, pe.Elem()))
+			p.postfixExpression(f, n.PostfixExpression, pe, exprValue, flags)
+			if !n.Expression.Operand.IsZero() {
+				p.nzUintptr(n, func() { p.expression(f, n.Expression, n.Expression.Operand.Type(), exprValue, flags) }, n.Expression.Operand)
+				if sz := pe.Elem().Size(); sz != 1 {
+					p.w("*%d", sz)
+				}
+			}
+			p.w("))")
+		case cc.Array:
+			defer p.w("%s", p.convert(n, n.Operand, t, flags))
+			p.w("*(*%s)(unsafe.Pointer(", p.typ(n, pe.Elem()))
+			p.postfixExpression(f, n.PostfixExpression, pe, exprDecay, flags)
+			if !n.Expression.Operand.IsZero() {
+				p.nzUintptr(n, func() { p.expression(f, n.Expression, n.Expression.Operand.Type(), exprValue, flags) }, n.Expression.Operand)
+				if sz := pe.Elem().Size(); sz != 1 {
+					p.w("*%d", sz)
+				}
+			}
+			p.w("))")
+		default:
+			panic(todo("", p.pos(n), pe, pe.Kind()))
+		}
+	}
+}
+
+func (p *project) postfixExpressionValueIndexArray(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression '[' Expression ']'
+	pe := n.PostfixExpression.Operand.Type()
+	switch n.Operand.Type().Kind() {
+	case cc.Array:
+		defer p.w("%s", p.convert(n, n.Operand, t, flags))
+		p.w("(")
+		defer p.w(")")
+		p.postfixExpression(f, n.PostfixExpression, pe, exprDecay, flags)
+		if !n.Expression.Operand.IsZero() {
+			p.nzUintptr(n, func() { p.expression(f, n.Expression, n.Expression.Operand.Type(), exprValue, flags) }, n.Expression.Operand)
+			if sz := pe.Elem().Size(); sz != 1 {
+				p.w("*%d", sz)
+			}
+		}
+	default:
+		defer p.w("%s", p.convert(n, n.Operand, t, flags))
+		p.postfixExpression(f, n.PostfixExpression, pe, mode, flags)
+		p.w("[")
+		p.expression(f, n.Expression, n.Expression.Operand.Type(), exprValue, flags)
+		p.w("]")
+	}
+}
+
+func (p *project) postfixExpressionValueSelect(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression '.' IDENTIFIER
+	switch k := p.opKind(f, n.PostfixExpression, n.PostfixExpression.Operand.Type()); k {
+	case opStruct:
+		p.postfixExpressionValueSelectStruct(f, n, t, mode, flags)
+	case opUnion:
+		p.postfixExpressionValueSelectUnion(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), k))
+	}
+}
+
+func (p *project) postfixExpressionValueSelectUnion(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression '.' IDENTIFIER
+	pe := n.PostfixExpression.Operand.Type()
+	fld := n.Field
+	switch {
+	case n.Operand.Type().IsBitFieldType():
+		p.w("(")
+		defer p.w("%s)", p.convertType(n, fld.Promote(), t, flags))
+		x := p.convertType(n, nil, fld.Promote(), flags)
+		p.w("*(*uint%d)(unsafe.Pointer(", fld.BitFieldBlockWidth())
+		p.postfixExpression(f, n.PostfixExpression, pe, exprAddrOf, flags)
+		p.bitFldOff(pe, n.Token2)
+		p.w("))&%#x>>%d%s", fld.Mask(), fld.BitFieldOffset(), x)
+		if fld.Type().IsSignedType() {
+			p.w("<<%d>>%[1]d", int(fld.Promote().Size()*8)-fld.BitFieldWidth())
+		}
+	case n.Operand.Type().Kind() == cc.Array:
+		p.postfixExpression(f, n.PostfixExpression, pe, exprAddrOf, flags)
+	default:
+		if fld.IsBitField() {
+			p.err(&n.Token2, "internal error, wrong function for accessing a bit field: %s", n.Token2.Value)
+		}
+		defer p.w("%s", p.convert(n, n.Operand, t, flags))
+		p.w("*(*%s)(unsafe.Pointer(", p.typ(n, n.Operand.Type()))
+		p.postfixExpression(f, n.PostfixExpression, pe, exprAddrOf, flags)
+		p.w("))")
+	}
+}
+
+func (p *project) postfixExpressionValueSelectStruct(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression '.' IDENTIFIER
+	pe := n.PostfixExpression.Operand.Type()
+	fld := n.Field
+	switch {
+	case n.Operand.Type().IsBitFieldType():
+		p.w("(")
+		defer p.w("%s)", p.convertType(n, fld.Promote(), t, flags))
+		x := p.convertType(n, nil, fld.Promote(), flags)
+		p.w("*(*uint%d)(unsafe.Pointer(", fld.BitFieldBlockWidth())
+		p.postfixExpression(f, n.PostfixExpression, pe, exprAddrOf, flags)
+		p.bitFldOff(pe, n.Token2)
+		p.w("))&%#x>>%d%s", fld.Mask(), fld.BitFieldOffset(), x)
+		if fld.Type().IsSignedType() {
+			p.w("<<%d>>%[1]d", int(fld.Promote().Size()*8)-fld.BitFieldWidth())
+		}
+	case n.Operand.Type().Kind() == cc.Array:
+		p.postfixExpression(f, n, t, exprDecay, flags)
+	case fld.InUnion():
+		defer p.w("%s", p.convert(n, n.Operand, t, flags))
+		p.w("*(*%s)(unsafe.Pointer(", p.typ(n, fld.Type()))
+		p.postfixExpression(f, n.PostfixExpression, pe, exprAddrOf, flags)
+		p.fldOff(pe, n.Token2)
+		p.w("))")
+	default:
+		if fld.IsBitField() {
+			p.err(&n.Token2, "internal error, wrong function for accessing a bit field: %s", n.Token2.Value)
+		}
+		defer p.w("%s", p.convert(n, n.Operand, t, flags))
+		p.postfixExpression(f, n.PostfixExpression, pe, exprSelect, flags)
+		p.w(".%s", p.fieldName(n, n.Token2.Value))
+	}
+}
+
+func (p *project) postfixExpressionLValue(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.PostfixExpressionPrimary: // PrimaryExpression
+		p.primaryExpression(f, n.PrimaryExpression, t, mode, flags)
+	case cc.PostfixExpressionIndex: // PostfixExpression '[' Expression ']'
+		p.postfixExpressionLValueIndex(f, n, t, mode, flags)
+	case cc.PostfixExpressionCall: // PostfixExpression '(' ArgumentExpressionList ')'
+		panic(todo("", p.pos(n)))
+	case cc.PostfixExpressionSelect: // PostfixExpression '.' IDENTIFIER
+		p.postfixExpressionLValueSelect(f, n, t, mode, flags)
+	case cc.PostfixExpressionPSelect: // PostfixExpression "->" IDENTIFIER
+		p.postfixExpressionLValuePSelect(f, n, t, mode, flags)
+	case cc.PostfixExpressionInc: // PostfixExpression "++"
+		p.postfixExpressionIncDec(f, n, "++", "+=", t, mode, flags)
+	case cc.PostfixExpressionDec: // PostfixExpression "--"
+		p.postfixExpressionIncDec(f, n, "--", "-=", t, mode, flags)
+	case cc.PostfixExpressionComplit: // '(' TypeName ')' '{' InitializerList ',' '}'
+		panic(todo("", p.pos(n)))
+	case cc.PostfixExpressionTypeCmp: // "__builtin_types_compatible_p" '(' TypeName ',' TypeName ')'
+		panic(todo("", p.pos(n)))
+	case cc.PostfixExpressionChooseExpr:
+		panic(todo("", p.pos(n)))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) postfixExpressionLValuePSelect(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression "->" IDENTIFIER
+	pe := n.PostfixExpression
+	switch k := p.opKind(f, n.PostfixExpression, n.PostfixExpression.Operand.Type().Elem()); k {
+	case opStruct:
+		if !p.inUnion(n, pe.Operand.Type().Elem(), n.Token2.Value) {
+			p.postfixExpressionLValuePSelectStruct(f, n, t, mode, flags)
+			break
+		}
+
+		p.w("*(*%s)(unsafe.Pointer(", p.typ(n, n.Operand.Type()))
+		p.postfixExpression(f, pe, pe.Operand.Type(), exprValue, flags)
+		p.fldOff(pe.Operand.Type().Elem(), n.Token2)
+		p.w("))")
+	case opUnion:
+		p.postfixExpressionLValuePSelectUnion(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), k))
+	}
+}
+
+func (p *project) postfixExpressionLValuePSelectUnion(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression "->" IDENTIFIER
+	fld := n.Field
+	if fld.Offset() != 0 {
+		p.err(&n.Token2, "internal error, union field with non-zero offset: %s %v", n.Token2.Value, fld.Offset())
+	}
+	switch {
+	case n.Operand.Type().IsBitFieldType():
+		panic(todo("", p.pos(n)))
+	default:
+		if fld.IsBitField() {
+			p.err(&n.Token2, "internal error, wrong function for accessing a bit field: %s", n.Token2.Value)
+		}
+		pe := n.PostfixExpression.Operand.Type()
+		defer p.w("%s", p.convert(n, n.Operand, t, flags))
+		p.w("(*(*%s)(unsafe.Pointer(", p.typ(n, n.Operand.Type()))
+		p.postfixExpression(f, n.PostfixExpression, pe, exprValue, flags)
+		p.w("/* .%s */", p.fieldName(n, n.Token2.Value))
+		p.w(")))")
+	}
+}
+
+func (p *project) postfixExpressionLValuePSelectStruct(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression "->" IDENTIFIER
+	fld := n.Field
+	pe := n.PostfixExpression.Operand.Type()
+	k := p.opKind(f, n.PostfixExpression, n.PostfixExpression.Operand.Type())
+	switch {
+	case n.Operand.Type().IsBitFieldType():
+		panic(todo("", p.pos(n)))
+	case k == opArray:
+		defer p.w("%s", p.convert(n, n.Operand, t, flags))
+		p.postfixExpression(f, n.PostfixExpression, pe, exprValue, flags)
+		p.w("[0].%s", p.fieldName(n, n.Token2.Value))
+	default:
+		if fld.IsBitField() {
+			p.err(&n.Token2, "internal error, wrong function for accessing a bit field: %s", n.Token2.Value)
+		}
+		defer p.w("%s", p.convert(n, n.Operand, t, flags))
+		p.w("(*%s)(unsafe.Pointer(", p.typ(n, pe.Elem()))
+		p.postfixExpression(f, n.PostfixExpression, pe, exprValue, flags)
+		p.w(")).%s", p.fieldName(n, n.Token2.Value))
+	}
+}
+
+func (p *project) postfixExpressionLValueIndex(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression '[' Expression ']'
+	switch k := p.opKind(f, n.PostfixExpression, n.PostfixExpression.Operand.Type()); k {
+	case opArray:
+		p.postfixExpressionLValueIndexArray(f, n, t, mode, flags)
+	case opNormal:
+		p.postfixExpressionLValueIndexNormal(f, n, t, mode, flags)
+	case opArrayParameter:
+		p.postfixExpressionLValueIndexArrayParameter(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), k))
+	}
+}
+
+func (p *project) postfixExpressionLValueIndexArrayParameter(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression '[' Expression ']'
+	defer p.w("%s", p.convert(n, n.Operand, t, flags))
+	pe := n.PostfixExpression.Operand.Type()
+	p.w("*(*%s)(unsafe.Pointer(", p.typ(n, pe.Elem()))
+	p.postfixExpression(f, n.PostfixExpression, pe, exprValue, flags)
+	if !n.Expression.Operand.IsZero() {
+		p.nzUintptr(n, func() { p.expression(f, n.Expression, n.Expression.Operand.Type(), exprValue, flags) }, n.Expression.Operand)
+		if sz := pe.Elem().Size(); sz != 1 {
+			p.w("*%d", sz)
+		}
+	}
+	p.w("))")
+}
+
+func (p *project) postfixExpressionLValueIndexNormal(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression '[' Expression ']'
+	switch {
+	case n.Operand.Type().Kind() == cc.Array:
+		panic(todo("", p.pos(n)))
+	default:
+		switch pe := n.PostfixExpression.Operand.Type(); pe.Kind() {
+		case cc.Ptr:
+			defer p.w("%s", p.convert(n, n.Operand, t, flags))
+			p.w("*(*%s)(unsafe.Pointer(", p.typ(n, pe.Elem()))
+			p.postfixExpression(f, n.PostfixExpression, pe, exprValue, flags)
+			if !n.Expression.Operand.IsZero() {
+				p.nzUintptr(n, func() { p.expression(f, n.Expression, n.Expression.Operand.Type(), exprValue, flags) }, n.Expression.Operand)
+				if sz := pe.Elem().Size(); sz != 1 {
+					p.w("*%d", sz)
+				}
+			}
+			p.w("))")
+		case cc.Array:
+			defer p.w("%s", p.convert(n, n.Operand, t, flags))
+			p.w("*(*%s)(unsafe.Pointer(", p.typ(n, pe.Elem()))
+			p.postfixExpression(f, n.PostfixExpression, pe, exprDecay, flags)
+			if !n.Expression.Operand.IsZero() {
+				p.nzUintptr(n, func() { p.expression(f, n.Expression, n.Expression.Operand.Type(), exprValue, flags) }, n.Expression.Operand)
+				if sz := pe.Elem().Size(); sz != 1 {
+					p.w("*%d", sz)
+				}
+			}
+			p.w("))")
+		default:
+			panic(todo("", p.pos(n), pe))
+		}
+	}
+}
+
+func (p *project) postfixExpressionLValueIndexArray(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression '[' Expression ']'
+	pe := n.PostfixExpression.Operand.Type()
+	p.postfixExpression(f, n.PostfixExpression, pe, mode, flags)
+	p.w("[")
+	p.expression(f, n.Expression, n.Expression.Operand.Type(), exprValue, flags)
+	p.w("]")
+}
+
+func (p *project) postfixExpressionLValueSelect(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression '.' IDENTIFIER
+	pe := n.PostfixExpression
+	switch k := p.opKind(f, pe, pe.Operand.Type()); k {
+	case opStruct:
+		if !p.inUnion(n, pe.Operand.Type(), n.Token2.Value) {
+			p.postfixExpressionLValueSelectStruct(f, n, t, mode, flags)
+			break
+		}
+
+		p.w("*(*%s)(unsafe.Pointer(", p.typ(n, n.Operand.Type()))
+		p.postfixExpression(f, pe, pe.Operand.Type(), exprAddrOf, flags)
+		p.fldOff(pe.Operand.Type(), n.Token2)
+		p.w("))")
+	case opUnion:
+		p.postfixExpressionLValueSelectUnion(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), k))
+	}
+}
+
+func (p *project) inUnion(n cc.Node, t cc.Type, fname cc.StringID) bool {
+	f, ok := t.FieldByName(fname)
+	if !ok {
+		p.err(n, "unknown field: %s", fname)
+		return false
+	}
+
+	return f.InUnion()
+}
+
+func (p *project) postfixExpressionLValueSelectUnion(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	fld := n.Field
+	pe := n.PostfixExpression.Operand.Type()
+	switch {
+	case pe.Kind() == cc.Array:
+		panic(todo("", p.pos(n)))
+	case n.Operand.Type().IsBitFieldType():
+		panic(todo("", p.pos(n)))
+	default:
+		if fld.IsBitField() {
+			p.err(&n.Token2, "internal error, wrong function for accessing a bit field: %s", n.Token2.Value)
+		}
+		p.w("*(*%s)(unsafe.Pointer(", p.typ(n, n.Operand.Type()))
+		p.postfixExpression(f, n.PostfixExpression, pe, exprAddrOf, flags)
+		nonZeroUintptr(fld.Offset())
+		p.w("))")
+	}
+}
+
+func (p *project) postfixExpressionLValueSelectStruct(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression '.' IDENTIFIER
+	fld := n.Field
+	switch {
+	case n.Operand.Type().IsBitFieldType():
+		panic(todo("", p.pos(n)))
+	default:
+		if fld.IsBitField() {
+			p.err(&n.Token2, "internal error, wrong function for accessing a bit field: %s", n.Token2.Value)
+		}
+		pe := n.PostfixExpression.Operand.Type()
+		p.postfixExpression(f, n.PostfixExpression, pe, exprSelect, flags)
+		p.w(".%s", p.fieldName(n, n.Token2.Value))
+	}
+}
+
+func (p *project) postfixExpressionIncDec(f *function, n *cc.PostfixExpression, oper, oper2 string, t cc.Type, mode exprMode, flags flags) {
+	switch mode {
+	case exprVoid:
+		p.postfixExpressionIncDecVoid(f, n, oper, oper2, t, mode, flags)
+	case exprLValue:
+		p.postfixExpressionIncDecLValue(f, n, oper, oper2, t, mode, flags)
+	case exprValue:
+		p.postfixExpressionIncDecValue(f, n, oper, oper2, t, mode, flags)
+	default:
+		panic(todo("", mode))
+	}
+}
+
+func (p *project) postfixExpressionIncDecValue(f *function, n *cc.PostfixExpression, oper, oper2 string, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression "++"
+	pe := n.PostfixExpression.Operand.Type()
+	switch k := p.opKind(f, n.PostfixExpression, pe); k {
+	case opNormal:
+		p.postfixExpressionIncDecValueNormal(f, n, oper, oper2, t, mode, flags)
+	case opBitfield:
+		p.postfixExpressionIncDecValueBitfield(f, n, oper, oper2, t, mode, flags)
+	case opArrayParameter:
+		p.postfixExpressionIncDecValueArrayParameter(f, n, oper, oper2, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), pe, pe.Kind(), k))
+	}
+}
+
+func (p *project) postfixExpressionIncDecValueArrayParameter(f *function, n *cc.PostfixExpression, oper, oper2 string, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression "++"
+	pe := n.PostfixExpression.Operand.Type()
+	defer p.w("%s", p.convert(n, n.PostfixExpression.Operand, t, flags))
+	x := "Dec"
+	if oper == "++" {
+		x = "Inc"
+	}
+	p.w("%sPost%s%s(&", p.task.crt, x, p.helperType(n, pe.Decay()))
+	p.postfixExpression(f, n.PostfixExpression, pe, exprLValue, flags)
+	p.w(", %d)", p.incDelta(n.PostfixExpression, pe))
+}
+
+func (p *project) postfixExpressionIncDecValueBitfield(f *function, n *cc.PostfixExpression, oper, oper2 string, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression "++"
+	pe := n.PostfixExpression.Operand.Type()
+	defer p.w("%s", p.convert(n, n.PostfixExpression.Operand, t, flags))
+	x := "Dec"
+	if oper == "++" {
+		x = "Inc"
+	}
+	bf := pe.BitField()
+	p.w("%sPost%sBitFieldPtr%d%s(", p.task.crt, x, bf.BitFieldBlockWidth(), p.bfHelperType(pe))
+	p.postfixExpression(f, n.PostfixExpression, pe, exprAddrOf, flags)
+	p.w(", %d, %d, %d, %#x)", p.incDelta(n.PostfixExpression, pe), bf.BitFieldBlockWidth(), bf.BitFieldOffset(), bf.Mask())
+}
+
+func (p *project) postfixExpressionIncDecValueNormal(f *function, n *cc.PostfixExpression, oper, oper2 string, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression "++"
+	pe := n.PostfixExpression.Operand.Type()
+	defer p.w("%s", p.convert(n, n.PostfixExpression.Operand, t, flags))
+	x := "Dec"
+	if oper == "++" {
+		x = "Inc"
+	}
+	if d := n.PostfixExpression.Declarator(); d != nil && p.isVolatileOrAtomic(d) {
+		p.w("%sPost%sAtomic%s(&", p.task.crt, x, p.helperType(n, pe))
+		var local *local
+		var tld *tld
+		if f != nil {
+			local = f.locals[d]
+		}
+		if local == nil {
+			tld = p.tlds[d]
+		}
+		switch {
+		case local != nil:
+			p.w("%s", local.name)
+		case tld != nil:
+			p.w("%s", tld.name)
+		default:
+			panic(todo(""))
+		}
+		p.w(", %d)", p.incDelta(n.PostfixExpression, pe))
+		return
+	}
+
+	p.w("%sPost%s%s(&", p.task.crt, x, p.helperType(n, pe))
+	p.postfixExpression(f, n.PostfixExpression, pe, exprLValue, flags)
+	p.w(", %d)", p.incDelta(n.PostfixExpression, pe))
+}
+
+func (p *project) postfixExpressionIncDecLValue(f *function, n *cc.PostfixExpression, oper, oper2 string, t cc.Type, mode exprMode, flags flags) {
+	switch k := p.opKind(f, n, n.Operand.Type()); k {
+	case opNormal:
+		p.postfixExpressionIncDecLValueNormal(f, n, oper, oper2, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), k))
+	}
+}
+
+func (p *project) postfixExpressionIncDecLValueNormal(f *function, n *cc.PostfixExpression, oper, oper2 string, t cc.Type, mode exprMode, flags flags) {
+	pe := n.PostfixExpression.Operand.Type()
+	defer p.w("%s", p.convert(n, n.PostfixExpression.Operand, t, flags))
+	x := "Dec"
+	if oper == "++" {
+		x = "Inc"
+	}
+	p.w("%sPost%s%s(&", p.task.crt, x, p.helperType(n, pe))
+	p.postfixExpression(f, n.PostfixExpression, pe, exprLValue, flags)
+	p.w(", %d)", p.incDelta(n.PostfixExpression, pe))
+}
+
+func (p *project) postfixExpressionIncDecVoid(f *function, n *cc.PostfixExpression, oper, oper2 string, t cc.Type, mode exprMode, flags flags) {
+	switch k := p.opKind(f, n, n.Operand.Type()); k {
+	case opNormal:
+		p.postfixExpressionIncDecVoidNormal(f, n, oper, oper2, t, mode, flags)
+	case opBitfield:
+		p.postfixExpressionIncDec(f, n, oper, oper2, t, exprValue, flags)
+	default:
+		panic(todo("", n.Position(), k))
+	}
+}
+
+func (p *project) postfixExpressionIncDecVoidNormal(f *function, n *cc.PostfixExpression, oper, oper2 string, t cc.Type, mode exprMode, flags flags) {
+	if d := n.PostfixExpression.Declarator(); d != nil && p.isVolatileOrAtomic(d) {
+		switch d.Type().Size() {
+		case 4, 8:
+			if !d.Type().IsIntegerType() {
+				p.err(n, "unsupported volatile declarator type: %v", d.Type())
+				return
+			}
+
+			if f != nil {
+				if local := f.locals[d]; local != nil {
+					if local.isPinned {
+						panic(todo(""))
+					}
+
+					p.w("atomic.Add%s(&%s, ", p.helperType(n, d.Type()), local.name)
+					switch oper {
+					case "++":
+						// ok
+					case "--":
+						p.w("-")
+					default:
+						p.err(n, "unsupported volatile declarator operation: %v", oper)
+					}
+					p.w("%d)", p.incDelta(n, d.Type()))
+					return
+				}
+			}
+
+			if tld := p.tlds[d]; tld != nil {
+				p.w("atomic.Add%s(&%s, ", p.helperType(n, d.Type()), tld.name)
+				switch oper {
+				case "++":
+					// ok
+				case "--":
+					p.w("-")
+				default:
+					p.err(n, "unsupported volatile declarator operation: %v", oper)
+				}
+				p.w("%d)", p.incDelta(n, d.Type()))
+				return
+			}
+
+			panic(todo("", n.Position(), d.Position()))
+		default:
+			p.err(n, "unsupported volatile declarator size: %v", d.Type().Size())
+			return
+		}
+	}
+
+	pe := n.PostfixExpression.Operand.Type().Decay()
+	p.postfixExpression(f, n.PostfixExpression, pe, exprLValue, flags)
+	if pe.IsIntegerType() || pe.Kind() == cc.Ptr && p.incDelta(n, pe) == 1 {
+		p.w("%s", oper)
+		return
+	}
+
+	switch pe.Kind() {
+	case cc.Ptr, cc.Float, cc.Double:
+		p.w("%s %d", oper2, p.incDelta(n, pe))
+		return
+	}
+
+	panic(todo("", n.Position(), pe, pe.Kind()))
+}
+
+func (p *project) incDelta(n cc.Node, t cc.Type) uintptr {
+	if t.IsArithmeticType() {
+		return 1
+	}
+
+	if t.Kind() == cc.Ptr || t.Kind() == cc.Array {
+		return t.Elem().Size()
+	}
+
+	panic(todo("", n.Position(), t.Kind()))
+}
+
+func (p *project) bitFldOff(t cc.Type, tok cc.Token) {
+	var off uintptr
+	fld, ok := t.FieldByName(tok.Value)
+	switch {
+	case ok && !fld.IsBitField():
+		panic(todo("%v: internal error: bitFdlOff must not be used with non bit fields", origin(2)))
+	case !ok:
+		p.err(&tok, "uknown field: %s", tok.Value)
+	default:
+		off = fld.BitFieldBlockFirst().Offset()
+	}
+	if off != 0 {
+		p.w("+%d", off)
+	}
+	p.w("/* &.%s */", tok.Value)
+}
+
+func (p *project) fldOff(t cc.Type, tok cc.Token) {
+	if t.Kind() == cc.Ptr {
+		t = t.Elem()
+	}
+	var off uintptr
+	fld, ok := t.FieldByName(tok.Value)
+	switch {
+	case ok && fld.IsBitField():
+		panic(todo("%v: internal error: fdlOff must not be used with bit fields", origin(2)))
+	case !ok:
+		p.err(&tok, "uknown field: %s", tok.Value)
+	default:
+		off = fld.Offset()
+	}
+	if off != 0 {
+		p.w("+%d", off)
+	}
+	p.w("/* &.%s */", tok.Value)
+}
+
+func (p *project) postfixExpressionCall(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression '(' ArgumentExpressionList ')'
+	switch mode {
+	case exprVoid:
+		p.postfixExpressionCallVoid(f, n, t, mode, flags)
+	case exprValue:
+		p.postfixExpressionCallValue(f, n, t, mode, flags)
+	case exprBool:
+		p.postfixExpressionCallBool(f, n, t, mode, flags)
+	default:
+		panic(todo("", mode))
+	}
+}
+
+func (p *project) postfixExpressionCallBool(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression '(' ArgumentExpressionList ')'
+	p.w("(")
+	defer p.w(")")
+	defer p.w(" != 0")
+	if d := n.PostfixExpression.Declarator(); d != nil {
+		switch d.Name() {
+		case idVaArg:
+			if !f.vaType.IsScalarType() {
+				panic(todo("", f.vaType))
+			}
+
+			lhs := n.ArgumentExpressionList.AssignmentExpression
+			p.w("%sVa%s(&", p.task.crt, p.helperType(n, f.vaType))
+			p.assignmentExpression(f, lhs, lhs.Operand.Type(), exprLValue, flags)
+			p.w(")")
+			return
+		case idAtomicLoadN:
+			p.atomicLoadN(f, n, t, mode, flags)
+			return
+		case idBuiltinConstantPImpl:
+			p.w("%v", n.Operand.Value())
+			return
+		}
+	}
+
+	var va uintptr
+	if f != nil {
+		va = f.vaLists[n]
+	}
+	p.postfixExpression(f, n.PostfixExpression, n.PostfixExpression.Operand.Type(), exprFunc, flags)
+	p.argumentExpressionList(f, n.PostfixExpression, n.ArgumentExpressionList, va)
+}
+
+func (p *project) postfixExpressionCallValue(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression '(' ArgumentExpressionList ')'
+	defer p.w("%s", p.convert(n, n.Operand, t, flags))
+	if d := n.PostfixExpression.Declarator(); d != nil {
+		switch d.Name() {
+		case idVaEnd:
+			p.w("_ = ")
+			arg := n.ArgumentExpressionList.AssignmentExpression
+			p.assignmentExpression(f, arg, arg.Operand.Type(), exprValue, flags)
+			return
+		case idVaStart:
+			lhs := n.ArgumentExpressionList.AssignmentExpression
+			p.assignmentExpression(f, lhs, lhs.Operand.Type(), exprLValue, flags)
+			p.w(" = %s", f.vaName)
+			return
+		case idVaArg:
+			if !f.vaType.IsScalarType() {
+				panic(todo("", f.vaType))
+			}
+
+			lhs := n.ArgumentExpressionList.AssignmentExpression
+			p.w("%sVa%s(&", p.task.crt, p.helperType(n, f.vaType))
+			p.assignmentExpression(f, lhs, lhs.Operand.Type(), exprLValue, flags)
+			p.w(")")
+			return
+		case idAtomicLoadN:
+			p.atomicLoadN(f, n, t, mode, flags)
+			return
+		case idAddOverflow:
+			p.addOverflow(f, n, t, mode, flags)
+			return
+		case idSubOverflow:
+			p.subOverflow(f, n, t, mode, flags)
+			return
+		case idMulOverflow:
+			p.mulOverflow(f, n, t, mode, flags)
+			return
+		case idBuiltinConstantPImpl:
+			p.w("%v", n.Operand.Value())
+			return
+		}
+	}
+	var va uintptr
+	if f != nil {
+		va = f.vaLists[n]
+	}
+	p.postfixExpression(f, n.PostfixExpression, n.PostfixExpression.Operand.Type(), exprFunc, flags)
+	p.argumentExpressionList(f, n.PostfixExpression, n.ArgumentExpressionList, va)
+}
+
+// bool __builtin_mul_overflow (type1 a, type2 b, type3 *res)
+func (p *project) mulOverflow(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	args := p.argList(n.ArgumentExpressionList)
+	if len(args) != 3 {
+		p.err(n, "expected 3 arguments in call to __builtin_mul_overflow")
+		return
+	}
+
+	pt := args[2].Operand.Type()
+	if pt.Kind() != cc.Ptr {
+		p.err(n, "invalid argument of __builtin_mul_overflow (expected pointer): %s", pt)
+		return
+	}
+
+	vt := pt.Elem()
+	switch {
+	case vt.IsIntegerType():
+		switch vt.Size() {
+		case 1, 2, 4, 8, 16:
+			p.w("%sX__builtin_mul_overflow%s", p.task.crt, p.helperType(n, vt))
+		default:
+			p.err(n, "invalid argument of __builtin_mul_overflow: %v, elem kind %v", pt, vt.Kind())
+			return
+		}
+		p.w("(%s", f.tlsName)
+		types := []cc.Type{vt, vt, pt}
+		for i, v := range args[:3] {
+			p.w(", ")
+			p.assignmentExpression(f, v, types[i], exprValue, flags)
+		}
+		p.w(")")
+		return
+	}
+
+	p.err(n, "invalid arguments of __builtin_mul_overflow: (%v, %v, %v)", args[0].Operand.Type(), args[1].Operand.Type(), args[2].Operand.Type())
+}
+
+// bool __builtin_sub_overflow (type1 a, type2 b, type3 *res)
+func (p *project) subOverflow(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	args := p.argList(n.ArgumentExpressionList)
+	if len(args) != 3 {
+		p.err(n, "expected 3 arguments in call to __builtin_sub_overflow")
+		return
+	}
+
+	pt := args[2].Operand.Type()
+	if pt.Kind() != cc.Ptr {
+		p.err(n, "invalid argument of __builtin_sub_overflow (expected pointer): %s", pt)
+		return
+	}
+
+	vt := pt.Elem()
+	switch {
+	case vt.IsIntegerType():
+		switch vt.Size() {
+		case 1, 2, 4, 8:
+			p.w("%sX__builtin_sub_overflow%s", p.task.crt, p.helperType(n, vt))
+		default:
+			p.err(n, "invalid argument of __builtin_sub_overflow: %v, elem kind %v", pt, vt.Kind())
+			return
+		}
+		p.w("(%s", f.tlsName)
+		types := []cc.Type{vt, vt, pt}
+		for i, v := range args[:3] {
+			p.w(", ")
+			p.assignmentExpression(f, v, types[i], exprValue, flags)
+		}
+		p.w(")")
+		return
+	}
+
+	p.err(n, "invalid arguments of __builtin_sub_overflow: (%v, %v, %v)", args[0].Operand.Type(), args[1].Operand.Type(), args[2].Operand.Type())
+}
+
+// bool __builtin_add_overflow (type1 a, type2 b, type3 *res)
+func (p *project) addOverflow(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	args := p.argList(n.ArgumentExpressionList)
+	if len(args) != 3 {
+		p.err(n, "expected 3 arguments in call to __builtin_add_overflow")
+		return
+	}
+
+	pt := args[2].Operand.Type()
+	if pt.Kind() != cc.Ptr {
+		p.err(n, "invalid argument of __builtin_add_overflow (expected pointer): %s", pt)
+		return
+	}
+
+	vt := pt.Elem()
+	switch {
+	case vt.IsIntegerType():
+		switch vt.Size() {
+		case 1, 2, 4, 8:
+			p.w("%sX__builtin_add_overflow%s", p.task.crt, p.helperType(n, vt))
+		default:
+			p.err(n, "invalid argument of __builtin_add_overflow: %v, elem kind %v", pt, vt.Kind())
+			return
+		}
+		p.w("(%s", f.tlsName)
+		types := []cc.Type{vt, vt, pt}
+		for i, v := range args[:3] {
+			p.w(", ")
+			p.assignmentExpression(f, v, types[i], exprValue, flags)
+		}
+		p.w(")")
+		return
+	}
+
+	p.err(n, "invalid arguments of __builtin_add_overflow: (%v, %v, %v)", args[0].Operand.Type(), args[1].Operand.Type(), args[2].Operand.Type())
+}
+
+// type __atomic_load_n (type *ptr, int memorder)
+func (p *project) atomicLoadN(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	args := p.argList(n.ArgumentExpressionList)
+	if len(args) != 2 {
+		p.err(n, "expected 2 arguments in call to __atomic_load_n")
+		return
+	}
+
+	pt := args[0].Operand.Type()
+	if pt.Kind() != cc.Ptr {
+		p.err(n, "invalid argument of __atomic_load_n (expected pointer): %s", pt)
+		return
+	}
+
+	vt := pt.Elem()
+	switch {
+	case vt.IsIntegerType():
+		var s string
+		switch {
+		case vt.IsSignedType():
+			s = "Int"
+		default:
+			s = "Uint"
+		}
+		switch vt.Size() {
+		case 2, 4, 8:
+			p.w("%sAtomicLoadN%s%d", p.task.crt, s, 8*vt.Size())
+		default:
+			p.err(n, "invalid argument of __atomic_load_n: %v, elem kind %v", pt, vt.Kind())
+			return
+		}
+		types := []cc.Type{pt, p.intType}
+		p.w("(")
+		for i, v := range args[:2] {
+			if i != 0 {
+				p.w(", ")
+			}
+			p.assignmentExpression(f, v, types[i], exprValue, flags)
+		}
+		p.w(")")
+		return
+	case vt.Kind() == cc.Ptr:
+		panic(todo("", pt, vt))
+	}
+
+	p.err(n, "invalid first argument of __atomic_load_n: %v, elem kind %v", pt, vt.Kind())
+}
+
+func (p *project) postfixExpressionCallVoid(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	// PostfixExpression '(' ArgumentExpressionList ')'
+	if d := n.PostfixExpression.Declarator(); d != nil {
+		switch d.Name() {
+		case idVaEnd:
+			p.w("_ = ")
+			arg := n.ArgumentExpressionList.AssignmentExpression
+			p.assignmentExpression(f, arg, arg.Operand.Type(), exprValue, flags)
+			return
+		case idVaStart:
+			lhs := n.ArgumentExpressionList.AssignmentExpression
+			p.assignmentExpression(f, lhs, lhs.Operand.Type(), exprLValue, flags)
+			p.w(" = %s", f.vaName)
+			return
+		case idVaArg:
+			if !f.vaType.IsScalarType() {
+				panic(todo("", f.vaType))
+			}
+
+			lhs := n.ArgumentExpressionList.AssignmentExpression
+			p.w("%sVa%s(&", p.task.crt, p.helperType(n, f.vaType))
+			p.assignmentExpression(f, lhs, lhs.Operand.Type(), exprLValue, flags)
+			p.w(")")
+			return
+		case idAtomicStoreN:
+			p.atomicStoreN(f, n, t, mode, flags)
+			return
+		case idMulOverflow:
+			p.mulOverflow(f, n, t, mode, flags)
+			return
+		}
+	}
+	var va uintptr
+	if f != nil {
+		va = f.vaLists[n]
+	}
+	p.postfixExpression(f, n.PostfixExpression, n.PostfixExpression.Operand.Type(), exprFunc, flags)
+	p.argumentExpressionList(f, n.PostfixExpression, n.ArgumentExpressionList, va)
+}
+
+// void __atomic_store_n (type *ptr, type val, int memorder)
+func (p *project) atomicStoreN(f *function, n *cc.PostfixExpression, t cc.Type, mode exprMode, flags flags) {
+	args := p.argList(n.ArgumentExpressionList)
+	if len(args) != 3 {
+		p.err(n, "expected 3 arguments in call to __atomic_store_n")
+		return
+	}
+
+	pt := args[0].Operand.Type()
+	if pt.Kind() != cc.Ptr {
+		p.err(n, "invalid first argument of __atomic_store_n (expected pointer): %s", pt)
+		return
+	}
+
+	vt := args[1].Operand.Type()
+	switch {
+	case vt.IsIntegerType():
+		var s string
+		switch {
+		case vt.IsSignedType():
+			s = "Int"
+		default:
+			s = "Uint"
+		}
+		switch vt.Size() {
+		case 2, 4, 8:
+			p.w("%sAtomicStoreN%s%d", p.task.crt, s, 8*vt.Size())
+		default:
+			p.err(n, "invalid arguments of __atomic_store_n: (%v, %v), element kind %v", pt, vt, vt.Kind())
+			return
+		}
+		p.w("(")
+		types := []cc.Type{pt, vt, p.intType}
+		for i, v := range args[:3] {
+			if i != 0 {
+				p.w(", ")
+			}
+			if i == 1 {
+				p.w("%s(", strings.ToLower(p.helperType(n, vt)))
+			}
+			p.assignmentExpression(f, v, types[i], exprValue, flags)
+			if i == 1 {
+				p.w(")")
+			}
+		}
+		p.w(")")
+		return
+	case vt.Kind() == cc.Ptr:
+		panic(todo("", pt, vt))
+	}
+
+	p.err(n, "invalid arguments of __atomic_store_n: (%v, %v), element kind %v", pt, vt, vt.Kind())
+}
+
+func (p *project) argList(n *cc.ArgumentExpressionList) (r []*cc.AssignmentExpression) {
+	for ; n != nil; n = n.ArgumentExpressionList {
+		r = append(r, n.AssignmentExpression)
+	}
+	return r
+}
+
+func (p *project) argumentExpressionList(f *function, pe *cc.PostfixExpression, n *cc.ArgumentExpressionList, bpOff uintptr) {
+	switch {
+	case f == nil:
+		p.w("(nil")
+	default:
+		p.w("(%s", f.tlsName)
+	}
+	ft := funcType(pe.Operand.Type())
+	isVariadic := ft.IsVariadic()
+	params := ft.Parameters()
+	if len(params) == 1 && params[0].Type().Kind() == cc.Void {
+		params = nil
+	}
+	var args []*cc.AssignmentExpression
+	for ; n != nil; n = n.ArgumentExpressionList {
+		args = append(args, n.AssignmentExpression)
+	}
+	if len(args) < len(params) {
+		panic(todo("", p.pos(n)))
+	}
+
+	va := true
+	if len(args) > len(params) && !isVariadic {
+		var a []string
+		for _, v := range args {
+			a = append(a, v.Operand.Type().String())
+		}
+		sargs := strings.Join(a, ",")
+		switch d := pe.Declarator(); {
+		case d == nil:
+			p.err(pe, "too many arguments (%s) in call to %s", sargs, ft)
+		default:
+			p.err(pe, "too many arguments (%s) in call to %s of type %s", sargs, d.Name(), ft)
+		}
+		va = false
+	}
+
+	paren := ""
+	for i, arg := range args {
+		p.w(",%s", tidyComment(" ", arg))
+		mode := exprValue
+		if at := arg.Operand.Type(); at.Kind() == cc.Array {
+			mode = exprDecay
+		}
+		switch {
+		case i < len(params):
+			switch pt := params[i].Type(); {
+			case isTransparentUnion(params[i].Type()):
+				p.callArgTransparentUnion(f, arg, pt)
+			default:
+				p.assignmentExpression(f, arg, arg.Promote(), mode, 0)
+			}
+		case va && i == len(params):
+			p.w("%sVaList(%s%s, ", p.task.crt, f.bpName, nonZeroUintptr(bpOff))
+			paren = ")"
+			fallthrough
+		default:
+			var flags flags
+			if arg.Promote().IsIntegerType() {
+				switch x := arg.Operand.Value().(type) {
+				case cc.Int64Value:
+					if x < mathutil.MinInt || x > mathutil.MaxInt {
+						flags |= fForceConv
+					}
+				case cc.Uint64Value:
+					if x > mathutil.MaxInt {
+						flags |= fForceConv
+					}
+				}
+			}
+			p.assignmentExpression(f, arg, arg.Promote(), mode, flags)
+		}
+	}
+	if isVariadic && len(args) == len(params) {
+		p.w(", 0")
+	}
+	p.w("%s)", paren)
+}
+
+// https://gcc.gnu.org/onlinedocs/gcc-3.3/gcc/Type-Attributes.html
+//
+// transparent_union
+//
+// This attribute, attached to a union type definition, indicates that any
+// function parameter having that union type causes calls to that function to
+// be treated in a special way.
+//
+// First, the argument corresponding to a transparent union type can be of any
+// type in the union; no cast is required. Also, if the union contains a
+// pointer type, the corresponding argument can be a null pointer constant or a
+// void pointer expression; and if the union contains a void pointer type, the
+// corresponding argument can be any pointer expression. If the union member
+// type is a pointer, qualifiers like const on the referenced type must be
+// respected, just as with normal pointer conversions.
+//
+// Second, the argument is passed to the function using the calling conventions
+// of first member of the transparent union, not the calling conventions of the
+// union itself. All members of the union must have the same machine
+// representation; this is necessary for this argument passing to work
+// properly.
+//
+// Transparent unions are designed for library functions that have multiple
+// interfaces for compatibility reasons. For example, suppose the wait function
+// must accept either a value of type int * to comply with Posix, or a value of
+// type union wait * to comply with the 4.1BSD interface. If wait's parameter
+// were void *, wait would accept both kinds of arguments, but it would also
+// accept any other pointer type and this would make argument type checking
+// less useful. Instead,  might define the interface as follows:
+//
+//	typedef union
+//		{
+//			int *__ip;
+//			union wait *__up;
+//		} wait_status_ptr_t __attribute__ ((__transparent_union__));
+//
+//	pid_t wait (wait_status_ptr_t);
+//
+// This interface allows either int * or union wait * arguments to be passed,
+// using the int * calling convention. The program can call wait with arguments
+// of either type:
+//
+//	int w1 () { int w; return wait (&w); }
+//	int w2 () { union wait w; return wait (&w); }
+//
+// With this interface, wait's implementation might look like this:
+//
+//	pid_t wait (wait_status_ptr_t p)
+//	{
+//		return waitpid (-1, p.__ip, 0);
+//	}
+func (p *project) callArgTransparentUnion(f *function, n *cc.AssignmentExpression, pt cc.Type) {
+	if pt.Kind() != cc.Union {
+		panic(todo("internal error"))
+	}
+
+	ot := n.Operand.Type()
+	switch k := pt.UnionCommon(); k {
+	case cc.Ptr:
+		if ot.Kind() != k {
+			panic(todo("", n.Position(), k, pt))
+		}
+
+		p.assignmentExpression(f, n, ot, exprValue, 0)
+	default:
+		panic(todo("", n.Position(), k, pt))
+	}
+}
+
+func isTransparentUnion(t cc.Type) (r bool) {
+	for _, v := range attrs(t) {
+		cc.Inspect(v, func(n cc.Node, _ bool) bool {
+			if x, ok := n.(*cc.AttributeValue); ok && x.Token.Value == idTransparentUnion {
+				r = true
+				return false
+			}
+
+			return true
+		})
+	}
+	return r
+}
+
+func attrs(t cc.Type) []*cc.AttributeSpecifier {
+	if a := t.Attributes(); len(a) != 0 {
+		return a
+	}
+
+	if t.IsAliasType() {
+		if a := t.Alias().Attributes(); len(a) != 0 {
+			return a
+		}
+
+		return t.AliasDeclarator().Type().Attributes()
+	}
+
+	return nil
+}
+
+func (p *project) nzUintptr(n cc.Node, f func(), op cc.Operand) {
+	if op.Type().IsIntegerType() {
+		switch {
+		case op.IsZero():
+			return
+		case op.Value() != nil:
+			switch x := op.Value().(type) {
+			case cc.Int64Value:
+				if x > 0 && uint64(x) <= 1<<(8*p.ptrSize)-1 {
+					p.w("+%d", x)
+					return
+				}
+			case cc.Uint64Value:
+				if uint64(x) <= 1<<(8*p.ptrSize)-1 {
+					p.w("+%d", x)
+					return
+				}
+			}
+
+			p.w(" +%sUintptrFrom%s(", p.task.crt, p.helperType(n, op.Type()))
+		default:
+			p.w(" +uintptr(")
+		}
+
+		f()
+		p.w(")")
+		return
+	}
+
+	panic(todo("", p.pos(n)))
+}
+
+func (p *project) primaryExpression(f *function, n *cc.PrimaryExpression, t cc.Type, mode exprMode, flags flags) {
+	switch mode {
+	case exprLValue:
+		p.primaryExpressionLValue(f, n, t, mode, flags)
+	case exprValue:
+		p.primaryExpressionValue(f, n, t, mode, flags)
+	case exprFunc:
+		p.primaryExpressionFunc(f, n, t, mode, flags)
+	case exprAddrOf:
+		p.primaryExpressionAddrOf(f, n, t, mode, flags)
+	case exprSelect:
+		p.primaryExpressionSelect(f, n, t, mode, flags)
+	case exprPSelect:
+		p.primaryExpressionPSelect(f, n, t, mode, flags)
+	case exprBool:
+		p.primaryExpressionBool(f, n, t, mode, flags)
+	case exprVoid:
+		p.primaryExpressionVoid(f, n, t, mode, flags)
+	case exprDecay:
+		p.primaryExpressionDecay(f, n, t, mode, flags)
+	default:
+		panic(todo("", n.Position(), mode))
+	}
+}
+
+func (p *project) primaryExpressionDecay(f *function, n *cc.PrimaryExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.PrimaryExpressionIdent: // IDENTIFIER
+		switch d := n.Declarator(); {
+		case d != nil:
+			p.declarator(n, f, d, t, mode, flags)
+		default:
+			panic(todo("", p.pos(n)))
+		}
+	case cc.PrimaryExpressionInt: // INTCONST
+		p.intConst(n, n.Token.Src.String(), n.Operand, t, flags)
+	case cc.PrimaryExpressionFloat: // FLOATCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionEnum: // ENUMCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionChar: // CHARCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionLChar: // LONGCHARCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionString: // STRINGLITERAL
+		p.w("%s", p.stringLiteral(n.Operand.Value()))
+	case cc.PrimaryExpressionLString: // LONGSTRINGLITERAL
+		p.w("%s", p.wideStringLiteral(n.Operand.Value(), 0))
+	case cc.PrimaryExpressionExpr: // '(' Expression ')'
+		p.expression(f, n.Expression, t, mode, flags)
+	case cc.PrimaryExpressionStmt: // '(' CompoundStatement ')'
+		p.err(n, "statement expressions not supported")
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) primaryExpressionVoid(f *function, n *cc.PrimaryExpression, t cc.Type, mode exprMode, flags flags) {
+
+	switch n.Case {
+	case cc.PrimaryExpressionIdent: // IDENTIFIER
+		p.w("_ = ")
+		p.primaryExpression(f, n, n.Operand.Type(), exprValue, flags)
+	case cc.PrimaryExpressionInt, // INTCONST
+		cc.PrimaryExpressionFloat,   // FLOATCONST
+		cc.PrimaryExpressionEnum,    // ENUMCONST
+		cc.PrimaryExpressionChar,    // CHARCONST
+		cc.PrimaryExpressionLChar,   // LONGCHARCONST
+		cc.PrimaryExpressionString,  // STRINGLITERAL
+		cc.PrimaryExpressionLString: // LONGSTRINGLITERAL
+
+		// nop
+	case cc.PrimaryExpressionExpr: // '(' Expression ')'
+		p.expression(f, n.Expression, n.Expression.Operand.Type(), mode, flags)
+	case cc.PrimaryExpressionStmt: // '(' CompoundStatement ')'
+		p.compoundStatement(f, n.CompoundStatement, "", true, false, 0)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) primaryExpressionBool(f *function, n *cc.PrimaryExpression, t cc.Type, mode exprMode, flags flags) {
+	if n.Case != cc.PrimaryExpressionExpr {
+		p.w("(")
+		defer p.w(")")
+	}
+
+	if n.Case != cc.PrimaryExpressionExpr {
+		defer p.w(" != 0")
+	}
+	switch n.Case {
+	case cc.PrimaryExpressionIdent: // IDENTIFIER
+		switch d := n.Declarator(); {
+		case d != nil:
+			p.declarator(n, f, d, d.Type(), exprValue, flags)
+		default:
+			panic(todo("", p.pos(n)))
+		}
+	case cc.PrimaryExpressionInt: // INTCONST
+		p.intConst(n, n.Token.Src.String(), n.Operand, n.Operand.Type(), flags)
+	case cc.PrimaryExpressionFloat: // FLOATCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionEnum: // ENUMCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionChar: // CHARCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionLChar: // LONGCHARCONST
+		p.charConst(n, n.Token.Src.String(), n.Operand, t, flags)
+	case cc.PrimaryExpressionString: // STRINGLITERAL
+		p.w(" 1 ")
+	case cc.PrimaryExpressionLString: // LONGSTRINGLITERAL
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionExpr: // '(' Expression ')'
+		p.w("(")
+		defer p.w(")")
+		p.expression(f, n.Expression, t, mode, flags)
+	case cc.PrimaryExpressionStmt: // '(' CompoundStatement ')'
+		p.w("func() %v {", p.typ(n, n.CompoundStatement.Operand.Type()))
+		p.compoundStatement(f, n.CompoundStatement, "", true, false, exprValue)
+		p.w("}()")
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) primaryExpressionPSelect(f *function, n *cc.PrimaryExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.PrimaryExpressionIdent: // IDENTIFIER
+		switch d := n.Declarator(); {
+		case d != nil:
+			switch k := p.declaratorKind(d); k {
+			case opArray:
+				panic(todo("", p.pos(n)))
+				p.primaryExpression(f, n, t, exprDecay, flags)
+			default:
+				p.declarator(n, f, d, t, mode, flags)
+			}
+		default:
+			panic(todo("", p.pos(n)))
+		}
+	case cc.PrimaryExpressionInt: // INTCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionFloat: // FLOATCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionEnum: // ENUMCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionChar: // CHARCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionLChar: // LONGCHARCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionString: // STRINGLITERAL
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionLString: // LONGSTRINGLITERAL
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionExpr: // '(' Expression ')'
+		p.expression(f, n.Expression, t, mode, flags)
+	case cc.PrimaryExpressionStmt: // '(' CompoundStatement ')'
+		p.err(n, "statement expressions not supported")
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) primaryExpressionSelect(f *function, n *cc.PrimaryExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.PrimaryExpressionIdent: // IDENTIFIER
+		switch d := n.Declarator(); {
+		case d != nil:
+			p.declarator(n, f, d, t, mode, flags)
+		default:
+			panic(todo("", p.pos(n)))
+		}
+	case cc.PrimaryExpressionInt: // INTCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionFloat: // FLOATCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionEnum: // ENUMCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionChar: // CHARCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionLChar: // LONGCHARCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionString: // STRINGLITERAL
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionLString: // LONGSTRINGLITERAL
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionExpr: // '(' Expression ')'
+		p.expression(f, n.Expression, t, mode, flags)
+	case cc.PrimaryExpressionStmt: // '(' CompoundStatement ')'
+		p.err(n, "statement expressions not supported")
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) primaryExpressionAddrOf(f *function, n *cc.PrimaryExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.PrimaryExpressionIdent: // IDENTIFIER
+		switch d := n.Declarator(); {
+		case d != nil:
+			p.declarator(n, f, d, t, mode, flags)
+		default:
+			panic(todo("", p.pos(n)))
+		}
+	case cc.PrimaryExpressionInt: // INTCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionFloat: // FLOATCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionEnum: // ENUMCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionChar: // CHARCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionLChar: // LONGCHARCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionString: // STRINGLITERAL
+		p.w("%s", p.stringLiteral(n.Operand.Value()))
+	case cc.PrimaryExpressionLString: // LONGSTRINGLITERAL
+		p.w("%s", p.wideStringLiteral(n.Operand.Value(), 0))
+	case cc.PrimaryExpressionExpr: // '(' Expression ')'
+		p.expression(f, n.Expression, t, mode, flags)
+	case cc.PrimaryExpressionStmt: // '(' CompoundStatement ')'
+		p.err(n, "statement expressions not supported")
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) primaryExpressionFunc(f *function, n *cc.PrimaryExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.PrimaryExpressionIdent: // IDENTIFIER
+		p.fnVal(n, f, func() { p.primaryExpression(f, n, n.Operand.Type(), exprValue, flags) }, n.Declarator(), n.Operand.Type(), 0, mode, flags)
+	case cc.PrimaryExpressionInt: // INTCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionFloat: // FLOATCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionEnum: // ENUMCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionChar: // CHARCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionLChar: // LONGCHARCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionString: // STRINGLITERAL
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionLString: // LONGSTRINGLITERAL
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionExpr: // '(' Expression ')'
+		p.expression(f, n.Expression, t, mode, flags)
+	case cc.PrimaryExpressionStmt: // '(' CompoundStatement ')'
+		p.err(n, "statement expressions not supported")
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func cmpNormalizeValue(v cc.Value) cc.Value {
+	switch x := v.(type) {
+	case cc.Int64Value:
+		if x >= 0 {
+			return cc.Uint64Value(x)
+		}
+	}
+	return v
+}
+
+func (p *project) primaryExpressionValue(f *function, n *cc.PrimaryExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.PrimaryExpressionIdent: // IDENTIFIER
+		switch d := n.Declarator(); {
+		case d != nil:
+			p.declarator(n, f, d, t, mode, flags)
+		default:
+			panic(todo("", p.pos(n)))
+		}
+	case cc.PrimaryExpressionInt: // INTCONST
+		if m := n.Token.Macro(); m != 0 {
+			if d := p.defines[m]; d.name != "" {
+				if cmpNormalizeValue(n.Operand.Value()) == cmpNormalizeValue(d.value) {
+					defer p.w("%s", p.convert(n, n.Operand, t, flags))
+					p.w(" %s ", d.name)
+					break
+				}
+
+				p.w("/* %s */", m)
+			}
+		}
+
+		p.intConst(n, n.Token.Src.String(), n.Operand, t, flags)
+	case cc.PrimaryExpressionFloat: // FLOATCONST
+		//TODO use #define
+		p.floatConst(n, n.Token.Src.String(), n.Operand, t, flags)
+	case cc.PrimaryExpressionEnum: // ENUMCONST
+		en := n.ResolvedTo().(*cc.Enumerator)
+		if n.ResolvedIn().Parent() == nil {
+			if nm := p.enumConsts[en.Token.Value]; nm != "" {
+				p.w(" %s ", nm)
+				break
+			}
+		}
+
+		p.intConst(n, "", n.Operand, t, flags)
+		p.w("/* %s */", en.Token.Value)
+	case cc.PrimaryExpressionChar: // CHARCONST
+		p.charConst(n, n.Token.Src.String(), n.Operand, t, flags)
+	case cc.PrimaryExpressionLChar: // LONGCHARCONST
+		p.charConst(n, n.Token.Src.String(), n.Operand, t, flags)
+	case cc.PrimaryExpressionString: // STRINGLITERAL
+		p.w("%s", p.stringLiteral(n.Operand.Value()))
+	case cc.PrimaryExpressionLString: // LONGSTRINGLITERAL
+		p.w("%s", p.wideStringLiteral(n.Operand.Value(), 0))
+	case cc.PrimaryExpressionExpr: // '(' Expression ')'
+		p.w("(")
+		defer p.w(")")
+		p.expression(f, n.Expression, t, mode, flags)
+	case cc.PrimaryExpressionStmt: // '(' CompoundStatement ')'
+		p.statementExpression(f, n.CompoundStatement, t, mode, flags)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) statementExpression(f *function, n *cc.CompoundStatement, t cc.Type, mode exprMode, flags flags) {
+	defer p.w("%s", p.convert(n, n.Operand, t, flags))
+	p.w(" func() %v {", p.typ(n, n.Operand.Type()))
+	p.compoundStatement(f, n, "", true, false, mode)
+	p.w("}()")
+}
+
+func (p *project) primaryExpressionLValue(f *function, n *cc.PrimaryExpression, t cc.Type, mode exprMode, flags flags) {
+	switch n.Case {
+	case cc.PrimaryExpressionIdent: // IDENTIFIER
+		switch d := n.Declarator(); {
+		case d != nil:
+			p.declarator(n, f, d, t, mode, flags)
+		default:
+			panic(todo("", p.pos(n)))
+		}
+	case cc.PrimaryExpressionInt: // INTCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionFloat: // FLOATCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionEnum: // ENUMCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionChar: // CHARCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionLChar: // LONGCHARCONST
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionString: // STRINGLITERAL
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionLString: // LONGSTRINGLITERAL
+		panic(todo("", p.pos(n)))
+	case cc.PrimaryExpressionExpr: // '(' Expression ')'
+		p.w("(")
+		defer p.w(")")
+		p.expression(f, n.Expression, t, mode, flags)
+	case cc.PrimaryExpressionStmt: // '(' CompoundStatement ')'
+		p.err(n, "statement expressions not supported")
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) stringLiteralString(s string) string {
+	if p.pass1 {
+		return ""
+	}
+
+	id := cc.String(s)
+	off, ok := p.tsOffs[id]
+	if !ok {
+		off = uintptr(p.ts.Len())
+		p.ts.WriteString(s)
+		p.ts.WriteByte(0)
+		p.tsOffs[id] = off
+	}
+	return fmt.Sprintf("(%s%s)%s", p.tsNameP, nonZeroUintptr(off), p.stringSnippet(s))
+}
+
+func (p *project) stringLiteral(v cc.Value) string {
+	if p.pass1 {
+		return ""
+	}
+
+	switch x := v.(type) {
+	case cc.StringValue:
+		id := cc.StringID(x)
+		off, ok := p.tsOffs[id]
+		s := id.String()
+		if !ok {
+			off = uintptr(p.ts.Len())
+			p.ts.WriteString(s)
+			p.ts.WriteByte(0)
+			p.tsOffs[id] = off
+		}
+		return fmt.Sprintf("(%s%s)%s", p.tsNameP, nonZeroUintptr(off), p.stringSnippet(s))
+	default:
+		panic(todo("%T", x))
+	}
+}
+
+func (p *project) stringSnippet(s string) string {
+	s = strings.ReplaceAll(s, "*/", "*\\/")
+	const max = 16
+	switch {
+	case len(s) <= max:
+		return fmt.Sprintf("/* %q */", s)
+	default:
+		return fmt.Sprintf("/* %q */", s[:16]+"...")
+	}
+}
+
+func (p *project) wideStringLiteral(v cc.Value, pad int) string {
+	if p.pass1 {
+		return ""
+	}
+
+	switch x := v.(type) {
+	case cc.WideStringValue:
+		id := cc.StringID(x)
+		off, ok := p.tsWOffs[id]
+		if !ok {
+			off = p.wcharSize * uintptr(len(p.tsW))
+			s := []rune(id.String())
+			if pad != 0 {
+				s = append(s, make([]rune, pad)...)
+			}
+			p.tsW = append(p.tsW, s...)
+			p.tsW = append(p.tsW, 0)
+			p.tsWOffs[id] = off
+		}
+		return fmt.Sprintf("(%s%s)", p.tsWNameP, nonZeroUintptr(off))
+	default:
+		panic(todo("%T", x))
+	}
+}
+
+func (p *project) charConst(n cc.Node, src string, op cc.Operand, to cc.Type, flags flags) {
+	switch {
+	case to.IsArithmeticType():
+		defer p.w("%s", p.convert(n, op, to, flags))
+	case to.Kind() == cc.Ptr && op.IsZero():
+		p.w(" 0 ")
+		return
+	default:
+		panic(todo("%v: t %v, to %v, to.Alias() %v", n.Position(), op.Type(), to, to.Alias()))
+	}
+
+	r, mb, _, err := strconv.UnquoteChar(src[1:len(src)-1], '\'')
+	rValid := !mb && err == nil
+	var on uint64
+	switch x := op.Value().(type) {
+	case cc.Int64Value:
+		on = uint64(x)
+	case cc.Uint64Value:
+		on = uint64(x)
+	default:
+		panic(todo("%T(%v)", x, x))
+	}
+	var mask uint64
+	switch {
+	case !to.IsIntegerType():
+		// ok
+		if rValid { // Prefer original form
+			p.w("%s", src)
+			return
+		}
+
+		p.w("%d", on)
+		return
+	case to.IsSignedType():
+		var in int64
+		var ok bool
+		switch to.Size() {
+		case 1:
+			in = int64(int8(on))
+			ok = int8(on) >= 0
+		case 2:
+			in = int64(int16(on))
+			ok = int16(on) >= 0
+		case 4:
+			in = int64(int32(on))
+			ok = int32(on) >= 0
+		case 8:
+			in = int64(int64(on))
+			ok = in >= 0
+		default:
+			panic(todo("", op.Type().Size()))
+		}
+		if ok && rValid && uint64(in) == on { // Prefer original form
+			p.w("%s", src)
+			return
+		}
+
+		p.w("%d", in)
+	default:
+		switch to.Size() {
+		case 1:
+			mask = 0xff
+		case 2:
+			mask = 0xffff
+		case 4:
+			mask = 0xffffffff
+		case 8:
+			mask = 0xffffffffffffffff
+		default:
+			panic(todo("", op.Type().Size()))
+		}
+		if rValid && uint64(r)&mask == on { // Prefer original form
+			p.w("%s", src)
+			return
+		}
+
+		p.w("%d", on&mask)
+	}
+}
+
+func (p *project) floatConst(n cc.Node, src string, op cc.Operand, to cc.Type, flags flags) {
+	if flags&fForceRuntimeConv != 0 {
+		p.w("%s(", p.helperType2(n, op.Type(), to))
+		defer p.w(")")
+	}
+
+	bits := 64
+	switch to.Kind() {
+	case cc.Float:
+		bits = 32
+	}
+	src = strings.TrimRight(src, "flFL")
+	sn, err := strconv.ParseFloat(src, bits)
+	snValid := err == nil
+	switch x := op.Value().(type) {
+	case cc.Float64Value:
+		switch to.Kind() {
+		case cc.Double:
+			if snValid && sn == float64(x) { // Prefer original form.
+				p.w("%s", src)
+				return
+			}
+
+			p.w("math.Float64frombits(%#x)", math.Float64bits(float64(x)))
+		case cc.Float:
+			if snValid && float32(sn) == float32(x) { // Prefer original form.
+				p.w("%s", src)
+				return
+			}
+
+			p.w("math.Float32frombits(%#x)", math.Float32bits(float32(x)))
+		default:
+			defer p.w("%s", p.convert(n, op, to, 0))
+			if snValid && sn == float64(x) { // Prefer original form.
+				p.w("%s", src)
+				return
+			}
+
+			p.w("math.Float64frombits(%#x)", math.Float64bits(float64(x)))
+		}
+	case cc.Float32Value:
+		switch to.Kind() {
+		case cc.Double:
+			if snValid && float32(sn) == float32(x) { // Prefer original form.
+				p.w("%s", src)
+				return
+			}
+
+			p.w("math.Float64frombits(%#x)", math.Float64bits(float64(x)))
+		case cc.Float:
+			if snValid && float32(sn) == float32(x) { // Prefer original form.
+				p.w("%s", src)
+				return
+			}
+
+			p.w("math.Float32frombits(%#x)", math.Float32bits(float32(x)))
+		default:
+			if to.IsIntegerType() {
+				if s := p.float2Int(n, x, to); s != "" {
+					defer p.w("%s%s", s, p.convertType(n, op.Type(), to, 0))
+					break
+				}
+			}
+
+			defer p.w("%s", p.convert(n, op, to, 0))
+			if snValid && float32(sn) == float32(x) { // Prefer original form.
+				p.w("%s", src)
+				return
+			}
+
+			p.w("math.Float32frombits(%#x)", math.Float32bits(float32(x)))
+		}
+	default:
+		panic(todo("%T(%v)", x, x))
+	}
+}
+
+func (p *project) float2Int(n cc.Node, x cc.Float32Value, to cc.Type) string {
+	switch {
+	case to.IsSignedType():
+		limits := &signedSaturationLimits[to.Size()]
+		v := float64(x)
+		switch {
+		case math.IsNaN(v):
+			panic(todo("", p.pos(n)))
+		case math.IsInf(v, -1):
+			panic(todo("", p.pos(n)))
+		case math.IsInf(v, 1):
+			panic(todo("", p.pos(n)))
+		case v < limits.fmin:
+			return fmt.Sprint(limits.min)
+		case v > limits.fmax:
+			return fmt.Sprint(limits.max)
+		}
+	default:
+		limits := &unsignedSaturationLimits[to.Size()]
+		v := float64(x)
+		switch {
+		case math.IsNaN(v):
+			panic(todo("", p.pos(n)))
+		case math.IsInf(v, -1):
+			panic(todo("", p.pos(n)))
+		case math.IsInf(v, 1):
+			panic(todo("", p.pos(n)))
+		case v < 0:
+			return "0"
+		case v > limits.fmax:
+			return fmt.Sprint(limits.max)
+		}
+	}
+	return ""
+}
+
+type signedSaturationLimit struct {
+	fmin, fmax float64
+	min, max   int64
+}
+
+type unsignedSaturationLimit struct {
+	fmax float64
+	max  uint64
+}
+
+var (
+	signedSaturationLimits = [...]signedSaturationLimit{
+		1: {math.Nextafter(math.MinInt32, 0), math.Nextafter(math.MaxInt32, 0), math.MinInt32, math.MaxInt32},
+		2: {math.Nextafter(math.MinInt32, 0), math.Nextafter(math.MaxInt32, 0), math.MinInt32, math.MaxInt32},
+		4: {math.Nextafter(math.MinInt32, 0), math.Nextafter(math.MaxInt32, 0), math.MinInt32, math.MaxInt32},
+		8: {math.Nextafter(math.MinInt64, 0), math.Nextafter(math.MaxInt64, 0), math.MinInt64, math.MaxInt64},
+	}
+
+	unsignedSaturationLimits = [...]unsignedSaturationLimit{
+		1: {math.Nextafter(math.MaxUint32, 0), math.MaxUint32},
+		2: {math.Nextafter(math.MaxUint32, 0), math.MaxUint32},
+		4: {math.Nextafter(math.MaxUint32, 0), math.MaxUint32},
+		8: {math.Nextafter(math.MaxUint64, 0), math.MaxUint64},
+	}
+)
+
+func (p *project) intConst(n cc.Node, src string, op cc.Operand, to cc.Type, flags flags) {
+	ptr := to.Kind() == cc.Ptr
+	switch {
+	case to.IsArithmeticType():
+		// p.w("/*10568 %T(%#[1]x) %v -> %v */", op.Value(), op.Type(), to) //TODO-
+		if flags&fForceNoConv != 0 {
+			break
+		}
+
+		if !op.Type().IsSignedType() && op.Type().Size() == 8 && op.Value().(cc.Uint64Value) > math.MaxInt64 {
+			flags |= fForceRuntimeConv
+		}
+		defer p.w("%s", p.convert(n, op, to, flags))
+	case ptr:
+		p.w(" uintptr(")
+		defer p.w(")")
+		// ok
+	default:
+		panic(todo("%v: %v -> %v", pos(n), op.Type(), to))
+	}
+
+	src = strings.TrimRight(src, "luLU")
+	sn, err := strconv.ParseUint(src, 0, 64)
+	snValid := err == nil
+	var on uint64
+	switch x := op.Value().(type) {
+	case cc.Int64Value:
+		if x < 0 {
+			sn, err := strconv.ParseInt(src, 0, 64)
+			snValid := err == nil
+			if snValid && sn == int64(x) { // Prefer original form
+				p.w("%s", src)
+				return
+			}
+
+			p.w("%d", x)
+			return
+		}
+
+		on = uint64(x)
+	case cc.Uint64Value:
+		on = uint64(x)
+	default:
+		panic(todo("%T(%v)", x, x))
+	}
+
+	if snValid && sn == on { // Prefer original form
+		p.w("%s", src)
+		return
+	}
+
+	p.w("%d", on)
+}
+
+func (p *project) assignShiftOp(f *function, n *cc.AssignmentExpression, t cc.Type, mode exprMode, oper, oper2 string, flags flags) {
+	// UnaryExpression "<<=" AssignmentExpression etc.
+	switch mode {
+	case exprVoid:
+		p.assignShiftOpVoid(f, n, t, mode, oper, oper2, flags)
+	default:
+		panic(todo("", mode))
+	}
+}
+
+func (p *project) assignShiftOpVoid(f *function, n *cc.AssignmentExpression, t cc.Type, mode exprMode, oper, oper2 string, flags flags) {
+	// UnaryExpression "<<=" AssignmentExpression etc.
+	switch k := p.opKind(f, n.UnaryExpression, n.UnaryExpression.Operand.Type()); k {
+	case opNormal:
+		p.assignShiftOpVoidNormal(f, n, t, mode, oper, oper2, flags)
+	default:
+		panic(todo("", n.Position(), k))
+	}
+}
+
+func (p *project) assignShiftOpVoidNormal(f *function, n *cc.AssignmentExpression, t cc.Type, mode exprMode, oper, oper2 string, flags flags) {
+	switch {
+	case n.Operand.Type().IsBitFieldType():
+		panic(todo("", p.pos(n)))
+	default:
+		if d := n.UnaryExpression.Declarator(); d != nil {
+			switch d.Type().Kind() {
+			case cc.Int128, cc.UInt128:
+				p.declarator(n, f, d, d.Type(), exprLValue, flags)
+				p.w(".LValue%s(", oper2)
+				p.assignmentExpression(f, n.AssignmentExpression, p.intType, exprValue, flags)
+				p.w(")")
+				return
+			default:
+				p.declarator(n, f, d, d.Type(), exprLValue, flags)
+				p.w(" %s= ", oper)
+				p.assignmentExpression(f, n.AssignmentExpression, n.Promote(), exprValue, flags)
+				return
+			}
+		}
+
+		lhs := n.UnaryExpression
+		switch {
+		case lhs.Operand.Type().IsArithmeticType():
+			p.w("%sAssign%sPtr%s(", p.task.crt, oper2, p.helperType(n, lhs.Operand.Type()))
+			p.unaryExpression(f, lhs, lhs.Operand.Type(), exprAddrOf, flags)
+			p.w(", int(")
+			p.assignmentExpression(f, n.AssignmentExpression, n.Promote(), exprValue, flags)
+			p.w("))")
+		default:
+			panic(todo("", p.pos(n), lhs.Operand.Type()))
+		}
+	}
+}
+
+func (p *project) assignOp(f *function, n *cc.AssignmentExpression, t cc.Type, mode exprMode, oper, oper2 string, flags flags) {
+	// UnaryExpression "*=" AssignmentExpression etc.
+	switch mode {
+	case exprVoid:
+		p.assignOpVoid(f, n, t, mode, oper, oper2, flags)
+	case exprValue, exprCondReturn:
+		p.assignOpValue(f, n, t, mode, oper, oper2, flags)
+	default:
+		panic(todo("", n.Position(), mode))
+	}
+}
+
+func (p *project) assignOpValue(f *function, n *cc.AssignmentExpression, t cc.Type, mode exprMode, oper, oper2 string, flags flags) {
+	// UnaryExpression "*=" AssignmentExpression etc.
+	switch k := p.opKind(f, n.UnaryExpression, n.UnaryExpression.Operand.Type()); k {
+	case opNormal:
+		p.assignOpValueNormal(f, n, t, oper, oper2, mode, flags)
+	case opBitfield:
+		p.assignOpValueBitfield(f, n, t, oper, oper2, mode, flags)
+	default:
+		panic(todo("", n.Position(), k))
+	}
+}
+
+func (p *project) assignOpValueBitfield(f *function, n *cc.AssignmentExpression, t cc.Type, oper, oper2 string, mode exprMode, flags flags) {
+	// UnaryExpression "*=" AssignmentExpression etc.
+
+	asInt := oper2 == "Shl" || oper2 == "Shr"
+	if asInt {
+		panic(todo(""))
+	}
+
+	ot := n.Operand.Type()
+	lhs := n.UnaryExpression
+	bf := lhs.Operand.Type().BitField()
+	defer p.w("%s", p.convertType(n, ot, t, flags))
+	p.w(" func() %v {", p.typ(n, ot))
+	switch lhs.Case {
+	case cc.UnaryExpressionPostfix: // PostfixExpression
+		pe := n.UnaryExpression.PostfixExpression
+		switch pe.Case {
+		case cc.PostfixExpressionSelect: // PostfixExpression '.' IDENTIFIER
+			p.w("__p := ")
+			p.postfixExpression(f, pe, pe.Operand.Type(), exprAddrOf, flags)
+			p.w("; __v := ")
+			p.readBitfield(lhs, "__p", bf, ot)
+			p.w(" %s (", oper)
+			p.assignmentExpression(f, n.AssignmentExpression, ot, exprValue, flags)
+			p.w("); return %sAssignBitFieldPtr%d%s(__p, __v, %d, %d, %#x)", p.task.crt, bf.BitFieldBlockWidth(), p.bfHelperType(ot), bf.BitFieldWidth(), bf.BitFieldOffset(), bf.Mask())
+		case cc.PostfixExpressionPSelect: // PostfixExpression "->" IDENTIFIER
+			panic(todo("", p.pos(n)))
+		default:
+			panic(todo("", n.Position(), pe.Case))
+		}
+	default:
+		panic(todo("", n.Position(), lhs.Case))
+	}
+	p.w("}()")
+}
+
+func (p *project) readBitfield(n cc.Node, ptr string, bf cc.Field, promote cc.Type) {
+	bw := bf.BitFieldBlockWidth()
+	m := bf.Mask()
+	o := bf.BitFieldOffset()
+	w := bf.BitFieldWidth()
+	p.w("(%s(*(*uint%d)(unsafe.Pointer(%s))&%#x)", p.typ(n, promote), bw, ptr, m)
+	switch {
+	case bf.Type().IsSignedType():
+		bits := int(promote.Size()) * 8
+		p.w("<<%d>>%d)", bits-w-o, bits-w)
+	default:
+		p.w(">>%d)", o)
+	}
+}
+
+func (p *project) assignOpValueNormal(f *function, n *cc.AssignmentExpression, t cc.Type, oper, oper2 string, mode exprMode, flags flags) {
+	if mode == exprCondReturn {
+		p.w("return ")
+	}
+	asInt := oper2 == "Shl" || oper2 == "Shr"
+	lhs := n.UnaryExpression
+	// UnaryExpression "*=" AssignmentExpression etc.
+	if d := lhs.Declarator(); d != nil {
+		if local := f.locals[d]; local != nil && local.isPinned {
+			switch {
+			case lhs.Operand.Type().IsArithmeticType():
+				defer p.w("%s", p.convertType(n, lhs.Operand.Type(), t, flags))
+				p.w("%sAssign%sPtr%s(", p.task.crt, oper2, p.helperType(n, lhs.Operand.Type()))
+				p.unaryExpression(f, lhs, lhs.Operand.Type(), exprAddrOf, flags)
+				p.w(", ")
+				if asInt {
+					p.w("int(")
+				}
+				p.assignmentExpression(f, n.AssignmentExpression, lhs.Operand.Type(), exprValue, flags)
+				if asInt {
+					p.w(")")
+				}
+				p.w(")")
+			default:
+				panic(todo("", lhs.Operand.Type()))
+			}
+			return
+		}
+
+		switch {
+		case d.Type().Kind() == cc.Ptr:
+			defer p.w("%s", p.convertType(n, d.Type(), t, flags))
+			p.w("%sAssign%s%s(&", p.task.crt, oper2, p.helperType(n, d.Type()))
+			p.declarator(n, f, d, d.Type(), exprLValue, flags)
+			p.w(", ")
+			if dd := p.incDelta(d, d.Type()); dd != 1 {
+				p.w("%d*(", dd)
+				defer p.w(")")
+			}
+			p.assignmentExpression(f, n.AssignmentExpression, d.Type(), exprValue, flags)
+			p.w(")")
+		case d.Type().IsArithmeticType():
+			defer p.w("%s", p.convertType(n, d.Type(), t, flags))
+			p.w("%sAssign%s%s(&", p.task.crt, oper2, p.helperType(n, d.Type()))
+			p.declarator(n, f, d, d.Type(), exprLValue, flags)
+			p.w(", ")
+			if asInt {
+				p.w("int(")
+			}
+			p.assignmentExpression(f, n.AssignmentExpression, d.Type(), exprValue, flags)
+			p.w(")")
+			if asInt {
+				p.w(")")
+			}
+		default:
+			panic(todo("", p.pos(n), p.pos(d), d.Name()))
+		}
+		return
+	}
+
+	switch {
+	case lhs.Operand.Type().IsArithmeticType():
+		defer p.w("%s", p.convertType(n, lhs.Operand.Type(), t, flags))
+		p.w("%sAssign%sPtr%s(", p.task.crt, oper2, p.helperType(n, lhs.Operand.Type()))
+		p.unaryExpression(f, lhs, lhs.Operand.Type(), exprAddrOf, flags)
+		p.w(", ")
+		if asInt {
+			p.w("int(")
+		}
+		p.assignmentExpression(f, n.AssignmentExpression, lhs.Operand.Type(), exprValue, flags)
+		if asInt {
+			p.w(")")
+		}
+		p.w(")")
+	default:
+		panic(todo("", lhs.Operand.Type()))
+	}
+}
+
+func (p *project) assignOpVoid(f *function, n *cc.AssignmentExpression, t cc.Type, mode exprMode, oper, oper2 string, flags flags) {
+	// UnaryExpression "*=" AssignmentExpression etc.
+	switch k := p.opKind(f, n.UnaryExpression, n.UnaryExpression.Operand.Type()); k {
+	case opNormal:
+		p.assignOpVoidNormal(f, n, t, oper, oper2, mode, flags)
+	case opBitfield:
+		p.assignOpVoidBitfield(f, n, t, oper, oper2, mode, flags)
+	case opArrayParameter:
+		p.assignOpVoidArrayParameter(f, n, t, oper, oper2, mode, flags)
+	default:
+		panic(todo("", n.Position(), k))
+	}
+}
+
+func (p *project) assignOpVoidArrayParameter(f *function, n *cc.AssignmentExpression, t cc.Type, oper, oper2 string, mode exprMode, flags flags) {
+	// UnaryExpression "*=" AssignmentExpression etc.
+	if oper != "+" && oper != "-" {
+		panic(todo("", p.pos(n)))
+	}
+
+	d := n.UnaryExpression.Declarator()
+	switch local := f.locals[d]; {
+	case local != nil && local.isPinned:
+		p.w("*(*uintptr)(unsafe.Pointer(%s%s))", f.bpName, nonZeroUintptr(local.off))
+	default:
+		p.declarator(n, f, d, d.Type(), exprLValue, flags)
+	}
+
+	p.w(" %s= ", oper)
+	if dd := p.incDelta(d, d.Type()); dd != 1 {
+		p.w("%d*", dd)
+	}
+	p.w("uintptr(")
+	p.assignmentExpression(f, n.AssignmentExpression, n.AssignmentExpression.Operand.Type(), exprValue, flags)
+	p.w(")")
+}
+
+func (p *project) assignOpVoidBitfield(f *function, n *cc.AssignmentExpression, t cc.Type, oper, oper2 string, mode exprMode, flags flags) {
+	// UnaryExpression "*=" AssignmentExpression etc.
+	lhs := n.UnaryExpression
+	lt := lhs.Operand.Type()
+	switch lhs.Case {
+	case cc.UnaryExpressionPostfix: // PostfixExpression
+		pe := n.UnaryExpression.PostfixExpression
+		switch pe.Case {
+		case cc.PostfixExpressionSelect: // PostfixExpression '.' IDENTIFIER
+			bf := lt.BitField()
+			p.w("%sSetBitFieldPtr%d%s(", p.task.crt, bf.BitFieldBlockWidth(), p.bfHelperType(n.Promote()))
+			p.unaryExpression(f, lhs, lt, exprAddrOf, flags)
+			p.w(", (")
+			s := p.convertType(n, lt, n.Promote(), flags)
+			p.unaryExpression(f, lhs, lt, exprValue, flags)
+			p.w(")%s %s ", s, oper)
+			s = p.convertType(n, lt, n.Promote(), flags)
+			p.assignmentExpression(f, n.AssignmentExpression, n.Promote(), exprValue, flags)
+			p.w("%s", s)
+			p.w(", %d, %#x)", bf.BitFieldOffset(), bf.Mask())
+		case cc.PostfixExpressionPSelect: // PostfixExpression "->" IDENTIFIER
+			switch d := pe.PostfixExpression.Declarator(); {
+			case d != nil:
+				panic(todo("", p.pos(n)))
+			default:
+				panic(todo("", p.pos(n)))
+			}
+		default:
+			panic(todo("", n.Position(), pe.Case))
+		}
+	default:
+		panic(todo("", n.Position(), lhs.Case))
+	}
+}
+
+func (p *project) assignOpVoidNormal(f *function, n *cc.AssignmentExpression, t cc.Type, oper, oper2 string, mode exprMode, flags flags) {
+	// UnaryExpression "*=" AssignmentExpression etc.
+	rop := n.AssignmentExpression.Operand
+	if d := n.UnaryExpression.Declarator(); d != nil {
+		if local := f.locals[d]; local != nil && local.isPinned {
+			if p.isVolatileOrAtomic(d) {
+				panic(todo(""))
+			}
+
+			p.declarator(n, f, d, d.Type(), exprLValue, flags)
+			switch {
+			case d.Type().Kind() == cc.Ptr:
+				p.w(" %s= ", oper)
+				if dd := p.incDelta(d, d.Type()); dd != 1 {
+					p.w("%d*(", dd)
+					defer p.w(")")
+				}
+				defer p.w("%s", p.convert(n, rop.ConvertTo(n.Promote()), d.Type(), flags))
+				p.assignmentExpression(f, n.AssignmentExpression, n.Promote(), exprValue, flags)
+			case d.Type().IsArithmeticType():
+				p.w(" %s= ", oper)
+				defer p.w("%s", p.convert(n, rop.ConvertTo(n.Promote()), d.Type(), flags))
+				p.assignmentExpression(f, n.AssignmentExpression, n.Promote(), exprValue, flags)
+			default:
+				panic(todo("", n.Position(), d.Type().Kind()))
+			}
+			return
+		}
+
+		if p.isVolatileOrAtomic(d) {
+			var local *local
+			var tld *tld
+			var nm string
+			if f != nil {
+				if local = f.locals[d]; local != nil {
+					nm = local.name
+				}
+			}
+
+			if local == nil {
+				if tld = p.tlds[d]; tld == nil {
+					p.err(n, "%v: internal error (%v: %v)", n.Position(), d.Position(), d.Name())
+					return
+				}
+
+				nm = tld.name
+			}
+			var sign string
+			switch oper {
+			case "-":
+				sign = oper
+				fallthrough
+			case "+":
+				sz := d.Type().Size()
+				var ht string
+				switch sz {
+				case 4, 8:
+					if !d.Type().IsScalarType() {
+						p.err(n, "unsupported volatile declarator type: %v", d.Type())
+						break
+					}
+
+					ht = p.helperType(n, d.Type())
+				default:
+					p.err(n, "unsupported volatile declarator size: %v", sz)
+					return
+				}
+
+				if local != nil {
+					if local.isPinned {
+						panic(todo(""))
+					}
+				}
+
+				p.w("%sAtomicAdd%s(&%s, %s%s(", p.task.crt, ht, nm, sign, p.typ(n, d.Type()))
+				p.assignmentExpression(f, n.AssignmentExpression, n.Promote(), exprValue, flags)
+				p.w("))")
+				return
+			default:
+				p.warn(n, "unsupported volatile declarator operation: %v", oper)
+				p.w("%s = ", nm)
+				defer p.w("%s", p.convert(n, rop.ConvertTo(n.Promote()), d.Type(), flags))
+				p.declarator(n, f, d, n.Promote(), exprValue, flags)
+				p.w(" %s (", oper)
+				p.assignmentExpression(f, n.AssignmentExpression, n.Promote(), exprValue, flags)
+				p.w(")")
+				return
+			}
+		}
+
+		p.declarator(n, f, d, d.Type(), exprLValue, flags)
+		switch d.Type().Kind() {
+		case cc.Ptr:
+			if oper != "+" && oper != "-" {
+				panic(todo("", p.pos(n)))
+			}
+
+			p.w(" %s= ", oper)
+			if dd := p.incDelta(d, d.Type()); dd != 1 {
+				p.w("%d*(", dd)
+				defer p.w(")")
+			}
+			defer p.w("%s", p.convert(n, rop.ConvertTo(n.Promote()), d.Type(), flags))
+			p.assignmentExpression(f, n.AssignmentExpression, n.Promote(), exprValue, flags)
+		case cc.Int128, cc.UInt128:
+			p.w(" = ")
+			p.declarator(n, f, d, n.Promote(), exprValue, flags)
+			p.w(".%s(", oper2)
+			p.assignmentExpression(f, n.AssignmentExpression, n.Promote(), exprValue, flags)
+			p.w(")")
+		default:
+			p.w(" = ")
+			defer p.w("%s", p.convert(n, rop.ConvertTo(n.Promote()), d.Type(), flags))
+			p.declarator(n, f, d, n.Promote(), exprValue, flags)
+			p.w(" %s (", oper)
+			p.assignmentExpression(f, n.AssignmentExpression, n.Promote(), exprValue, flags)
+			p.w(")")
+		}
+		return
+	}
+
+	lhs := n.UnaryExpression
+	switch {
+	case lhs.Operand.Type().IsArithmeticType():
+		p.w("*(*%s)(unsafe.Pointer(", p.typ(n, lhs.Operand.Type()))
+		p.unaryExpression(f, lhs, lhs.Operand.Type(), exprAddrOf, flags)
+		p.w(")) %s= ", oper)
+		defer p.w("%s", p.convert(n, rop.ConvertTo(n.Promote()), lhs.Operand.Type(), flags))
+		p.w("(")
+		p.assignmentExpression(f, n.AssignmentExpression, n.Promote(), exprValue, flags)
+		p.w(")")
+	case lhs.Operand.Type().Kind() == cc.Ptr:
+		p.w("*(*%s)(unsafe.Pointer(", p.typ(n, lhs.Operand.Type()))
+		p.unaryExpression(f, lhs, lhs.Operand.Type(), exprAddrOf, flags)
+		p.w(")) %s= (", oper)
+		p.assignmentExpression(f, n.AssignmentExpression, lhs.Operand.Type(), exprValue, flags)
+		p.w(")")
+		if dd := p.incDelta(n, lhs.Operand.Type()); dd != 1 {
+			p.w("*%d", dd)
+		}
+	default:
+		panic(todo("", lhs.Operand.Type()))
+	}
+}
+
+func (p *project) warn(n cc.Node, s string, args ...interface{}) {
+	s = fmt.Sprintf(s, args...)
+	s = strings.TrimRight(s, "\t\n\r")
+	fmt.Fprintf(os.Stderr, "%v: warning: %s\n", n.Position(), s)
+}
+
+func (p *project) iterationStatement(f *function, n *cc.IterationStatement) {
+	sv := f.switchCtx
+	sv2 := f.continueCtx
+	sv3 := f.breakCtx
+	f.switchCtx = 0
+	f.continueCtx = 0
+	f.breakCtx = 0
+	defer func() {
+		f.breakCtx = sv3
+		f.continueCtx = sv2
+		f.switchCtx = sv
+	}()
+	p.w("%s", tidyComment("\n", n))
+	switch n.Case {
+	case cc.IterationStatementWhile: // "while" '(' Expression ')' Statement
+		if f.hasJumps {
+			// a:	if !expr goto b
+			//	stmt
+			//	goto a
+			// b:
+			a := f.flatLabel()
+			b := f.flatLabel()
+			f.continueCtx = a
+			f.breakCtx = b
+			p.w("__%d: if !(", a)
+			p.expression(f, n.Expression, n.Expression.Operand.Type(), exprBool, 0)
+			p.w(") { goto __%d };", b)
+			p.statement(f, n.Statement, false, false, false, 0)
+			p.w("; goto __%d; __%d:", a, b)
+			break
+		}
+
+		p.w("for ")
+		p.expression(f, n.Expression, n.Expression.Operand.Type(), exprBool, 0)
+		p.statement(f, n.Statement, true, false, false, 0)
+	case cc.IterationStatementDo: // "do" Statement "while" '(' Expression ')' ';'
+		if f.hasJumps {
+			// a:	stmt
+			// b:	if expr goto a // b is the continue label
+			// c:
+			a := f.flatLabel()
+			b := f.flatLabel()
+			c := f.flatLabel()
+			f.continueCtx = b
+			f.breakCtx = c
+			p.w("__%d:", a)
+			p.statement(f, n.Statement, false, false, false, 0)
+			p.w(";goto __%d; __%[1]d: if ", b)
+			p.expression(f, n.Expression, n.Expression.Operand.Type(), exprBool, 0)
+			p.w("{goto __%d};goto __%d;__%[2]d:", a, c)
+			break
+		}
+
+		v := "ok"
+		if !p.pass1 {
+			v = f.scope.take(cc.String(v))
+		}
+		p.w("for %v := true; %[1]v; %[1]v = ", v)
+		p.expression(f, n.Expression, n.Expression.Operand.Type(), exprBool, 0)
+		p.statement(f, n.Statement, true, false, false, 0)
+	case cc.IterationStatementFor: // "for" '(' Expression ';' Expression ';' Expression ')' Statement
+		if f.hasJumps || n.Expression3 != nil && n.Expression3.Case == cc.ExpressionComma {
+			//	expr
+			// a:	if !expr2 goto c
+			//	stmt
+			// b: 	expr3 // label for continue
+			//	goto a
+			// c:
+			a := f.flatLabel()
+			b := f.flatLabel()
+			f.continueCtx = b
+			c := f.flatLabel()
+			f.breakCtx = c
+			if n.Expression != nil {
+				p.expression(f, n.Expression, n.Expression.Operand.Type(), exprVoid, fNoCondAssignment)
+			}
+			semi := ""
+			if n.Expression != nil || n.Expression2 != nil || n.Expression3 != nil {
+				semi = ";"
+			}
+			p.w("%s__%d:", semi, a)
+			if n.Expression2 != nil {
+				p.w("if !(")
+				p.expression(f, n.Expression2, n.Expression2.Operand.Type(), exprBool, 0)
+				p.w(") { goto __%d }", c)
+			}
+			p.w("%s", semi)
+			p.statement(f, n.Statement, false, false, false, 0)
+			p.w(";goto __%d; __%[1]d:", b)
+			if n.Expression3 != nil {
+				p.expression(f, n.Expression3, n.Expression3.Operand.Type(), exprVoid, fNoCondAssignment)
+			}
+			p.w("%sgoto __%d; goto __%d;__%[3]d:", semi, a, c)
+			break
+		}
+
+		expr := true
+		if n.Expression != nil && n.Expression.Case == cc.ExpressionComma {
+			p.expression(f, n.Expression, n.Expression.Operand.Type(), exprVoid, 0)
+			p.w(";")
+			expr = false
+		}
+		p.w("for ")
+		if expr && n.Expression != nil {
+			p.expression(f, n.Expression, n.Expression.Operand.Type(), exprVoid, fNoCondAssignment)
+		}
+		p.w("; ")
+		if n.Expression2 != nil {
+			p.expression(f, n.Expression2, n.Expression2.Operand.Type(), exprBool, 0)
+		}
+		p.w("; ")
+		if n.Expression3 != nil {
+			p.expression(f, n.Expression3, n.Expression3.Operand.Type(), exprVoid, fNoCondAssignment)
+		}
+		p.statement(f, n.Statement, true, false, false, 0)
+	case cc.IterationStatementForDecl: // "for" '(' Declaration Expression ';' Expression ')' Statement
+		var ids []*cc.InitDeclarator
+		for list := n.Declaration.InitDeclaratorList; list != nil; list = list.InitDeclaratorList {
+			ids = append(ids, list.InitDeclarator)
+		}
+
+		//	declaration
+		// a:	if !expr goto c
+		//	stmt
+		// b: 	expr2 // label for continue
+		//	goto a
+		// c:
+		a := f.flatLabel()
+		b := f.flatLabel()
+		f.continueCtx = b
+		c := f.flatLabel()
+		f.breakCtx = c
+		p.w("{")
+		p.declaration(f, n.Declaration, false)
+		p.w(";")
+		p.w("__%d:", a)
+		if n.Expression != nil {
+			p.w("if !(")
+			p.expression(f, n.Expression, n.Expression.Operand.Type(), exprBool, 0)
+			p.w(") { goto __%d }", c)
+		}
+		p.w(";")
+		p.statement(f, n.Statement, false, false, false, 0)
+		p.w(";goto __%d; __%[1]d:", b)
+		if n.Expression2 != nil {
+			p.expression(f, n.Expression2, n.Expression2.Operand.Type(), exprVoid, fNoCondAssignment)
+		}
+		p.w("; goto __%d; goto __%d;__%[2]d:\n}", a, c)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) selectionStatement(f *function, n *cc.SelectionStatement) {
+	p.w("%s", tidyComment("\n", n))
+	switch n.Case {
+	case cc.SelectionStatementIf: // "if" '(' Expression ')' Statement
+		sv := f.ifCtx
+		f.ifCtx = n
+		defer func() { f.ifCtx = sv }()
+		if f.hasJumps {
+			// if !expr goto a
+			// stmt
+			// a:
+			f.ifCtx = n
+			a := f.flatLabel()
+			p.w("if !(")
+			p.expression(f, n.Expression, n.Expression.Operand.Type(), exprBool, 0)
+			p.w(") { goto __%d };", a)
+			p.statement(f, n.Statement, false, false, false, 0)
+			p.w(";__%d: ", a)
+			break
+		}
+
+		p.w("if ")
+		p.expression(f, n.Expression, n.Expression.Operand.Type(), exprBool, 0)
+		p.statement(f, n.Statement, true, false, false, 0)
+	case cc.SelectionStatementIfElse: // "if" '(' Expression ')' Statement "else" Statement
+		sv := f.ifCtx
+		f.ifCtx = n
+		defer func() { f.ifCtx = sv }()
+		if f.hasJumps {
+			// if !expr goto a
+			// stmt
+			// goto b
+			// a:
+			// stmt2
+			// b:
+			a := f.flatLabel()
+			b := f.flatLabel()
+			p.w("if !(")
+			p.expression(f, n.Expression, n.Expression.Operand.Type(), exprBool, 0)
+			p.w(") { goto __%d };", a)
+			p.statement(f, n.Statement, false, false, false, 0)
+			p.w(";goto __%d; __%d:", b, a)
+			p.statement(f, n.Statement2, false, false, false, 0)
+			p.w(";__%d:", b)
+			break
+		}
+
+		p.w("if ")
+		p.expression(f, n.Expression, n.Expression.Operand.Type(), exprBool, 0)
+		p.statement(f, n.Statement, true, false, false, 0)
+		p.w(" else ")
+		switch {
+		case p.isIfStmt(n.Statement2):
+			p.statement(f, n.Statement2, false, true, false, 0)
+		default:
+			p.statement(f, n.Statement2, true, false, false, 0)
+		}
+	case cc.SelectionStatementSwitch: // "switch" '(' Expression ')' Statement
+		sv := f.switchCtx
+		svBreakCtx := f.breakCtx
+		f.breakCtx = 0
+		defer func() {
+			f.switchCtx = sv
+			f.breakCtx = svBreakCtx
+		}()
+		if f.hasJumps {
+			f.switchCtx = inSwitchFlat
+			p.flatSwitch(f, n)
+			break
+		}
+
+		f.switchCtx = inSwitchFirst
+		p.w("switch ")
+		p.expression(f, n.Expression, n.Promote(), exprValue, 0)
+		p.statement(f, n.Statement, true, false, true, 0)
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+}
+
+func (p *project) isIfStmt(n *cc.Statement) bool {
+	if n.Case != cc.StatementSelection {
+		return false
+	}
+
+	switch n.SelectionStatement.Case {
+	case cc.SelectionStatementIf, cc.SelectionStatementIfElse:
+		return true
+	}
+
+	return false
+}
+
+func (p *project) flatSwitch(f *function, n *cc.SelectionStatement) {
+	if n.Statement.Case != cc.StatementCompound {
+		panic(todo("", p.pos(n)))
+	}
+
+	sv := f.block
+	f.block = f.blocks[n.Statement.CompoundStatement]
+	defer func() { f.block = sv }()
+	// "switch" '(' Expression ')' Statement
+	cases := n.Cases()
+	labels := map[*cc.LabeledStatement]int{}
+	svBreakCtx := f.breakCtx
+	f.breakCtx = f.flatLabel()
+	p.w("switch ")
+	p.expression(f, n.Expression, n.Promote(), exprValue, 0)
+	p.w("{")
+	for _, ls := range cases {
+		switch ls.Case {
+		case cc.LabeledStatementLabel: // IDENTIFIER ':' AttributeSpecifierList Statement
+			continue
+		case cc.LabeledStatementCaseLabel: // "case" ConstantExpression ':' Statement
+			p.w("%scase ", tidyComment("\n", ls))
+			p.constantExpression(f, ls.ConstantExpression, ls.ConstantExpression.Operand.Type(), exprValue, 0)
+			p.w(":")
+		case cc.LabeledStatementDefault: // "default" ':' Statement
+			p.w("%sdefault:", tidyComment("\n", ls))
+		case cc.LabeledStatementRange: // "case" ConstantExpression "..." ConstantExpression ':' Statement
+			panic(todo("", p.pos(n)))
+		default:
+			panic(todo("%v: internal error: %v", n.Position(), n.Case))
+		}
+		label := f.flatLabel()
+		labels[ls] = label
+		p.w("goto __%d;", label)
+	}
+	p.w("}; goto __%d;", f.breakCtx)
+	svLabels := f.flatSwitchLabels
+	f.flatSwitchLabels = labels
+	p.statement(f, n.Statement, false, true, false, 0)
+	f.flatSwitchLabels = svLabels
+	p.w("__%d:", f.breakCtx)
+	f.breakCtx = svBreakCtx
+}
+
+func (p *project) expressionStatement(f *function, n *cc.ExpressionStatement) {
+	p.w("%s", tidyComment("\n", n))
+	// Expression AttributeSpecifierList ';'
+	if n.Expression == nil {
+		return
+	}
+
+	p.expression(f, n.Expression, n.Expression.Operand.Type(), exprVoid, 0)
+}
+
+func (p *project) labeledStatement(f *function, n *cc.LabeledStatement) (r *cc.JumpStatement) {
+	if f.hasJumps { //TODO merge with ...Flat below
+		return p.labeledStatementFlat(f, n)
+	}
+
+	switch n.Case {
+	case cc.LabeledStatementLabel: // IDENTIFIER ':' AttributeSpecifierList Statement
+		if _, ok := f.unusedLabels[n.Token.Value]; ok {
+			p.w("goto %s;", f.labelNames[n.Token.Value])
+		}
+		p.w("%s%s:", comment("\n", n), f.labelNames[n.Token.Value])
+		r = p.statement(f, n.Statement, false, false, false, 0)
+	case
+		cc.LabeledStatementCaseLabel, // "case" ConstantExpression ':' Statement
+		cc.LabeledStatementDefault:   // "default" ':' Statement
+
+		p.labeledStatementCase(f, n)
+	case cc.LabeledStatementRange: // "case" ConstantExpression "..." ConstantExpression ':' Statement
+		panic(todo("", n.Position(), n.Case))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+	return r
+}
+
+func (p *project) labeledStatementFlat(f *function, n *cc.LabeledStatement) (r *cc.JumpStatement) {
+	switch n.Case {
+	case cc.LabeledStatementLabel: // IDENTIFIER ':' AttributeSpecifierList Statement
+		if _, ok := f.unusedLabels[n.Token.Value]; ok {
+			p.w("goto %s;", f.labelNames[n.Token.Value])
+		}
+		p.w("%s%s:", tidyComment("\n", n), f.labelNames[n.Token.Value])
+		r = p.statement(f, n.Statement, false, false, false, 0)
+	case
+		cc.LabeledStatementCaseLabel, // "case" ConstantExpression ':' Statement
+		cc.LabeledStatementDefault:   // "default" ':' Statement
+
+		p.labeledStatementCase(f, n)
+	case cc.LabeledStatementRange: // "case" ConstantExpression "..." ConstantExpression ':' Statement
+		panic(todo("", n.Position(), n.Case))
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+	return r
+}
+
+func (p *project) labeledStatementCase(f *function, n *cc.LabeledStatement) {
+	switch f.switchCtx {
+	case inSwitchFirst:
+		f.switchCtx = inSwitchCase
+	case inSwitchCase:
+		p.w("\nfallthrough;")
+	case inSwitchSeenBreak:
+		f.switchCtx = inSwitchCase
+	case inSwitchFlat:
+		// ok
+	default:
+		panic(todo("", n.Position(), f.switchCtx))
+	}
+	switch n.Case {
+	case cc.LabeledStatementCaseLabel: // "case" ConstantExpression ':' Statement
+		switch {
+		case f.switchCtx == inSwitchFlat:
+			p.w("%s__%d:", tidyComment("\n", n), f.flatSwitchLabels[n])
+		default:
+			p.w("%scase ", tidyComment("\n", n))
+			p.constantExpression(f, n.ConstantExpression, n.ConstantExpression.Operand.Type(), exprValue, 0)
+			p.w(":")
+		}
+	case cc.LabeledStatementDefault: // "default" ':' Statement
+		switch {
+		case f.switchCtx == inSwitchFlat:
+			p.w("%s__%d:", tidyComment("\n", n), f.flatSwitchLabels[n])
+		default:
+			p.w("%sdefault:", tidyComment("\n", n))
+		}
+	default:
+		panic(todo("%v: internal error: %v", n.Position(), n.Case))
+	}
+	p.statement(f, n.Statement, false, false, false, 0)
+}
+
+func (p *project) constantExpression(f *function, n *cc.ConstantExpression, t cc.Type, mode exprMode, flags flags) {
+	// ConditionalExpression
+	p.conditionalExpression(f, n.ConditionalExpression, t, mode, flags)
+}
+
+func (p *project) functionDefinitionSignature(n cc.Node, f *function, tld *tld) {
+	switch {
+	case f.mainSignatureForced:
+		p.w("%sfunc %s(%s *%sTLS, _ int32, _ uintptr) int32", tidyComment("\n", f.fndef), tld.name, f.tlsName, p.task.crt)
+	default:
+		p.w("%s", tidyComment("\n", f.fndef))
+		p.functionSignature(n, f, f.fndef.Declarator.Type(), tld.name)
+	}
+}
+
+func (p *project) functionSignature2(n cc.Node, f *function, t cc.Type, nm string) {
+	p.w("func %s", nm)
+	p.w("(_ *%sTLS", p.task.crt)
+	suffix := 1
+	for _, v := range t.Parameters() {
+		if v.Type().Kind() == cc.Void {
+			break
+		}
+
+		pn := "_"
+		if d := v.Declarator(); d != nil {
+			pn = d.Name().String()
+			if _, ok := reservedNames[pn]; ok {
+				pn += strconv.Itoa(suffix)
+				suffix++
+			}
+		}
+		p.w(", %s %s", pn, p.paramTyp(v.Declarator(), v.Type()))
+	}
+	if t.IsVariadic() {
+		p.w(", _ /* va_list */ uintptr")
+	}
+	p.w(")")
+	if rt := t.Result(); rt != nil && rt.Kind() != cc.Void {
+		p.w(" %s", p.typ(n, rt))
+	}
+}
+
+func (p *project) functionSignature(n cc.Node, f *function, t cc.Type, nm string) {
+	p.w("func")
+	if nm != "" {
+		p.w(" %s", nm)
+	}
+	switch {
+	case f == nil || nm == "":
+		p.w("(*%sTLS", p.task.crt)
+	default:
+		p.w("(%s *%sTLS", f.tlsName, p.task.crt)
+	}
+	for _, v := range t.Parameters() {
+		if v.Type().Kind() == cc.Void {
+			break
+		}
+
+		var pn string
+		if f != nil && nm != "" {
+			pn = "_"
+			if d := v.Declarator(); d != nil {
+				if local := f.locals[d]; local != nil {
+					pn = local.name
+				}
+			}
+		}
+		p.w(", %s %s", pn, p.paramTyp(v.Declarator(), v.Type()))
+	}
+	if t.IsVariadic() {
+		switch {
+		case f == nil || nm == "":
+			p.w(", uintptr")
+		default:
+			p.w(", %s uintptr", f.vaName)
+		}
+	}
+	p.w(")")
+	if rt := t.Result(); rt != nil && rt.Kind() != cc.Void {
+		p.w(" %s", p.typ(n, rt))
+	}
+}
+
+func (p *project) paramTyp(n cc.Node, t cc.Type) string {
+	if t.Kind() == cc.Array {
+		return "uintptr"
+	}
+
+	if isTransparentUnion(t) {
+		switch k := t.UnionCommon(); k {
+		case cc.Ptr:
+			return "uintptr"
+		default:
+			panic(todo("%v: %v %k", n, t, k))
+		}
+	}
+
+	return p.typ(n, t)
+}
+
+func (p *project) dbg(a ...interface{}) {
+	p.w("/*DBG.%v %v */", a, origin(2))
+}
+
+func (p *project) fnVal(n cc.Node, f *function, expr func(), exprDecl *cc.Declarator, exprType cc.Type, deref int, mode exprMode, flags flags) {
+	//  C type		Go type
+	//  fn			N/A: produce name from exprDecl
+	//  (*)()		func()
+	//  (**)()		*func()
+
+	if deref < 0 || deref > 1 {
+		panic(todo(""))
+	}
+
+	switch exprType.Kind() {
+	case cc.Function:
+		// C: fn
+		switch deref {
+		case 0:
+			p.declarator(n, f, exprDecl, exprType, mode, flags)
+		default:
+			panic(todo("", n.Position()))
+		}
+	case cc.Ptr:
+		switch et := exprType.Elem(); et.Kind() {
+		case cc.Function:
+			// C: (*)()
+			switch deref {
+			case 0:
+				// (*struct{ f func()})(unsafe.Pointer(&struct{uintptr}{fprintfptr})).f()
+				p.w("(*struct{ f ")
+				p.functionSignature(n, f, et, "")
+				p.w("})(unsafe.Pointer(&struct{uintptr}{")
+				expr()
+				p.w("})).f")
+			default:
+				p.declarator(n, f, exprDecl, et, mode, flags)
+			}
+		case cc.Ptr:
+			switch et2 := et.Elem(); et2.Kind() {
+			case cc.Function:
+				// C: (**)()
+				switch deref {
+				case 0:
+					panic(todo("", n.Position()))
+				default:
+					// (*struct{ f func()})(unsafe.Pointer(&struct{uintptr}{fprintfptr})).f()
+					p.w("(*(**struct{ f ")
+					p.functionSignature(n, f, et2, "")
+					p.w("})(unsafe.Pointer(&struct{uintptr}{")
+					expr()
+					p.w("}))).f")
+				}
+			default:
+				panic(todo("", n.Position(), et2.Kind(), deref))
+			}
+		default:
+			panic(todo("", n.Position(), et.Kind(), deref))
+		}
+	default:
+		panic(todo("", n.Position(), exprType.Kind(), deref))
+	}
+}
diff --git a/vendor/modernc.org/ccgo/v3/lib/init.go b/vendor/modernc.org/ccgo/v3/lib/init.go
new file mode 100644
index 00000000..db3ad913
--- /dev/null
+++ b/vendor/modernc.org/ccgo/v3/lib/init.go
@@ -0,0 +1,553 @@
+// Copyright 2020 The CCGO 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 ccgo // import "modernc.org/ccgo/v3/lib"
+
+import (
+	"fmt"
+	"sort"
+	"strings"
+
+	"modernc.org/cc/v3"
+)
+
+func isAggregateTypeOrUnion(t cc.Type) bool {
+	switch t.Kind() {
+	case cc.Struct, cc.Union, cc.Array:
+		return true
+	}
+
+	return false
+}
+
+// 6.7.8 Initialization
+func (p *project) initializer(f *function, n *cc.Initializer, t cc.Type, sc cc.StorageClass, tld *tld) {
+	lm := map[*cc.Initializer][]cc.StringID{}
+	tm := map[*cc.Initializer][]cc.StringID{}
+	s := p.initializerFlatten(n, lm, tm)
+	sort.Slice(s, func(i, j int) bool {
+		a := s[i]
+		b := s[j]
+		if a.Offset < b.Offset {
+			return true
+		}
+
+		if a.Offset > b.Offset {
+			return false
+		}
+
+		if a.Field == nil || b.Field == nil || !a.Field.IsBitField() || !b.Field.IsBitField() {
+			panic(todo("%v: internal error: off %#x, %v: off %#x, t %v", a.Position(), a.Offset, b.Position(), b.Offset, t))
+		}
+
+		return a.Field.BitFieldOffset() < b.Field.BitFieldOffset()
+	})
+	p.initializerInner("", 0, f, s, t, sc, tld, nil, lm, tm)
+}
+
+func (p *project) initializerInner(tag string, off uintptr, f *function, s []*cc.Initializer, t cc.Type, sc cc.StorageClass, tld *tld, patchField cc.Field, lm, tm map[*cc.Initializer][]cc.StringID) {
+	// 11: The initializer for a scalar shall be a single expression, optionally
+	// enclosed in braces. The initial value of the object is that of the
+	// expression (after conversion); the same type constraints and conversions as
+	// for simple assignment apply, taking the type of the scalar to be the
+	// unqualified version of its declared type.
+	if t.IsScalarType() && len(s) == 1 {
+		p.w("%s%s", tidyComment("", s[0]), tag)
+		switch {
+		case tld != nil && t.Kind() == cc.Ptr && s[0].AssignmentExpression.Operand.Value() == nil:
+			tld.patches = append(tld.patches, initPatch{t, s[0], patchField})
+			p.w(" 0 ")
+		default:
+			p.assignmentExpression(f, s[0].AssignmentExpression, t, exprValue, 0)
+		}
+		return
+	}
+
+	// 12: The rest of this subclause deals with initializers for objects that have
+	// aggregate or union type.
+
+	k := t.Kind()
+
+	// 13: The initializer for a structure or union object that has automatic
+	// storage duration shall be either an initializer list as described below, or
+	// a single expression that has compatible structure or union type. In the
+	// latter case, the initial value of the object, including unnamed members, is
+	// that of the expression.
+	if sc == cc.Automatic && len(s) == 1 {
+		switch k {
+		case cc.Struct, cc.Union:
+			if compatibleStructOrUnion(t, s[0].AssignmentExpression.Operand.Type()) {
+				p.w("%s%s", tidyComment("", s[0]), tag)
+				p.assignmentExpression(f, s[0].AssignmentExpression, t, exprValue, 0)
+				return
+			}
+		}
+	}
+
+	if k == cc.Array && len(s) == 1 {
+		et := t.Elem()
+		switch {
+		case isCharType(et):
+			// 14: An array of character type may be initialized by a character string
+			// literal, optionally enclosed in braces. Successive characters of the
+			// character string literal (including the terminating null character if there
+			// is room or if the array is of unknown size) initialize the elements of the
+			// array.
+			if x, ok := s[0].AssignmentExpression.Operand.Value().(cc.StringValue); ok {
+				p.w("%s%s", tidyComment("", s[0]), tag)
+				str := cc.StringID(x).String()
+				slen := uintptr(len(str)) + 1
+				alen := t.Len()
+				switch {
+				case alen < slen-1:
+					p.w("*(*%s)(unsafe.Pointer(%s))", p.typ(s[0], t), p.stringLiteralString(str[:alen]))
+				case alen < slen:
+					p.w("*(*%s)(unsafe.Pointer(%s))", p.typ(s[0], t), p.stringLiteralString(str))
+				default: // alen >= slen
+					p.w("*(*%s)(unsafe.Pointer(%s))", p.typ(s[0], t), p.stringLiteralString(str+strings.Repeat("\x00", int(alen-slen))))
+				}
+				return
+			}
+		case p.isWCharType(et):
+			// 15: An array with element type compatible with wchar_t may be initialized by
+			// a wide string literal, optionally enclosed in braces. Successive wide
+			// characters of the wide string literal (including the terminating null wide
+			// character if there is room or if the array is of unknown size) initialize
+			// the elements of the array.
+			if x, ok := s[0].AssignmentExpression.Operand.Value().(cc.WideStringValue); ok {
+				p.w("%s%s", tidyComment("", s[0]), tag)
+				str := []rune(cc.StringID(x).String())
+				slen := uintptr(len(str)) + 1
+				alen := t.Len()
+				switch {
+				case alen < slen-1:
+					panic(todo("", p.pos(s[0])))
+				case alen < slen:
+					p.w("*(*%s)(unsafe.Pointer(%s))", p.typ(s[0], t), p.wideStringLiteral(x, 0))
+				default: // alen >= slen
+					p.w("*(*%s)(unsafe.Pointer(%s))", p.typ(s[0], t), p.wideStringLiteral(x, int(alen-slen)))
+				}
+				return
+			}
+		}
+	}
+
+	// 16: Otherwise, the initializer for an object that has aggregate or union
+	// type shall be a brace-enclosed list of initializers for the elements or
+	// named members.
+	switch k {
+	case cc.Array:
+		p.initializerArray(tag, off, f, s, t, sc, tld, lm, tm)
+	case cc.Struct:
+		p.initializerStruct(tag, off, f, s, t, sc, tld, lm, tm)
+	case cc.Union:
+		p.initializerUnion(tag, off, f, s, t, sc, tld, lm, tm)
+	default:
+		panic(todo("%v: internal error: %v alias %v %v", s[0].Position(), t, t.Alias(), len(s)))
+	}
+}
+
+func (p *project) initializerArray(tag string, off uintptr, f *function, s []*cc.Initializer, t cc.Type, sc cc.StorageClass, tld *tld, lm, tm map[*cc.Initializer][]cc.StringID) {
+	if len(s) == 0 {
+		p.w("%s%s{}", tag, p.typ(nil, t))
+		return
+	}
+
+	et := t.Elem()
+	esz := et.Size()
+	s0 := s[0]
+	p.w("%s%s%s{", initComment(s0, lm), tag, p.typ(s0, t))
+	var a [][]*cc.Initializer
+	for len(s) != 0 {
+		s2, parts, _ := p.initializerArrayElement(off, s, esz)
+		s = s2
+		a = append(a, parts)
+	}
+	mustIndex := uintptr(len(a)) != t.Len()
+	var parts []*cc.Initializer
+	for _, parts = range a {
+		var comma *cc.Token
+		comma = parts[len(parts)-1].TrailingComma()
+		elemOff := (parts[0].Offset - off) / esz * esz
+		tag = ""
+		if mustIndex {
+			tag = fmt.Sprintf("%d:", elemOff/esz)
+		}
+		p.initializerInner(tag, off+elemOff, f, parts, et, sc, tld, nil, lm, tm)
+		p.preCommaSep(comma)
+		p.w(",")
+	}
+	p.w("%s}", initComment(parts[len(parts)-1], tm))
+}
+
+func initComment(n *cc.Initializer, m map[*cc.Initializer][]cc.StringID) string {
+	a := m[n]
+	if len(a) == 0 {
+		return ""
+	}
+
+	m[n] = a[1:]
+	return tidyCommentString(a[0].String())
+}
+
+func (p *project) initializerArrayElement(off uintptr, s []*cc.Initializer, elemSize uintptr) (r []*cc.Initializer, parts []*cc.Initializer, isZero bool) {
+	r = s
+	isZero = true
+	valueOff := s[0].Offset - off
+	elemOff := valueOff - valueOff%elemSize
+	nextOff := elemOff + elemSize
+	for len(s) != 0 {
+		if v := s[0]; v.Offset-off < nextOff {
+			s = s[1:]
+			parts = append(parts, v)
+			if !v.AssignmentExpression.Operand.IsZero() {
+				isZero = false
+			}
+			continue
+		}
+
+		break
+	}
+	return r[len(parts):], parts, isZero
+}
+
+func (p *project) initializerStruct(tag string, off uintptr, f *function, s []*cc.Initializer, t cc.Type, sc cc.StorageClass, tld *tld, lm, tm map[*cc.Initializer][]cc.StringID) {
+	if len(s) == 0 {
+		p.w("%s%s{}", tag, p.typ(nil, t))
+		return
+	}
+
+	if t.HasFlexibleMember() {
+		p.err(s[0], "flexible array members not supported")
+		return
+	}
+
+	p.w("%s%s%s{", initComment(s[0], lm), tag, p.typ(s[0], t))
+	var parts []*cc.Initializer
+	var isZero bool
+	var fld cc.Field
+	for len(s) != 0 {
+		var comma *cc.Token
+		s, fld, parts, isZero = p.structInitializerParts(off, s, t)
+		if isZero {
+			continue
+		}
+
+		if fld.Type().IsIncomplete() {
+			panic(todo(""))
+		}
+
+		comma = parts[len(parts)-1].TrailingComma()
+		tag = fmt.Sprintf("%s:", p.fieldName2(parts[0], fld))
+		ft := fld.Type()
+		switch {
+		case fld.IsBitField():
+			bft := p.bitFileType(parts[0], fld.BitFieldBlockWidth())
+			off0 := fld.Offset()
+			first := true
+			for _, v := range parts {
+				if v.AssignmentExpression.Operand.IsZero() {
+					continue
+				}
+
+				if !first {
+					p.w("|")
+				}
+				first = false
+				bitFld := v.Field
+				p.w("%s%s", tidyComment("", v.AssignmentExpression), tag)
+				tag = ""
+				p.assignmentExpression(f, v.AssignmentExpression, bft, exprValue, 0)
+				p.w("&%#x", uint64(1)<= fNext {
+			break
+		}
+
+		isZero = isZero && part.AssignmentExpression.Operand.IsZero()
+		parts = append(parts, part)
+		s = s[1:]
+	}
+	return s, fld, parts, isZero
+}
+
+func (p *project) containingStructField(part *cc.Initializer, off uintptr, t cc.Type) (f cc.Field, fOff, fNext uintptr) {
+	nf := t.NumField()
+	vOff := part.Offset
+	for i := []int{0}; i[0] < nf; i[0]++ {
+		f = t.FieldByIndex(i)
+		if f.IsBitField() && f.Name() == 0 { // Anonymous bit fields cannot be initialized.
+			continue
+		}
+
+		fOff = off + f.Offset()
+		switch {
+		case f.IsBitField():
+			fNext = fOff + uintptr(f.BitFieldBlockWidth())>>3
+		default:
+			fNext = fOff + f.Type().Size()
+		}
+		if vOff >= fOff && vOff < fNext {
+			return f, fOff, fNext
+		}
+	}
+
+	panic(todo("%v: internal error", pos(part)))
+}
+
+func (p *project) initializerUnion(tag string, off uintptr, f *function, s []*cc.Initializer, t cc.Type, sc cc.StorageClass, tld *tld, lm, tm map[*cc.Initializer][]cc.StringID) {
+	if len(s) == 0 {
+		p.w("%s%s{}", tag, p.typ(nil, t))
+		return
+	}
+
+	if t.HasFlexibleMember() {
+		p.err(s[0], "flexible array members not supported")
+		return
+	}
+
+	parts, isZero := p.initializerUnionField(off, s, t)
+	if len(parts) == 0 || isZero {
+		p.w("%s%s%s{", initComment(s[0], lm), tag, p.typ(s[0], t))
+		p.w("%s}", initComment(parts[len(parts)-1], tm))
+		return
+	}
+
+	p.w("%sfunc() (r %s) {", tag, p.typ(parts[0], t))
+	for _, part := range parts {
+		var ft cc.Type
+		fld := part.Field
+		if fld != nil && fld.IsBitField() {
+		}
+
+		if ft == nil {
+			ft = part.Type()
+		}
+		if ft.Kind() == cc.Array {
+			et := ft.Elem()
+			switch {
+			case isCharType(et):
+				switch x := part.AssignmentExpression.Operand.Value().(type) {
+				case cc.StringValue:
+					str := cc.StringID(x).String()
+					slen := uintptr(len(str)) + 1
+					alen := ft.Len()
+					switch {
+					case alen < slen-1:
+						p.w("copy(((*[%d]byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&r))+%d)))[:], (*[%d]byte)(unsafe.Pointer(%s))[:])\n", alen, part.Offset-off, alen, p.stringLiteralString(str[:alen]))
+					case alen < slen:
+						p.w("copy(((*[%d]byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&r))+%d)))[:], (*[%d]byte)(unsafe.Pointer(%s))[:])\n", alen, part.Offset-off, alen, p.stringLiteralString(str))
+					default: // alen >= slen
+						p.w("copy(((*[%d]byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&r))+%d)))[:], (*[%d]byte)(unsafe.Pointer(%s))[:])\n", alen, part.Offset-off, alen, p.stringLiteralString(str+strings.Repeat("\x00", int(alen-slen))))
+					}
+					continue
+				default:
+					panic(todo("%v: %v <- %T", pos(part), et, x))
+				}
+			case p.isWCharType(et):
+				panic(todo(""))
+			}
+			ft = et
+		}
+		switch {
+		case fld != nil && fld.IsBitField():
+			bft := p.bitFileType(part, fld.BitFieldBlockWidth())
+			p.w("*(*%s)(unsafe.Pointer(uintptr(unsafe.Pointer(&r))+%d)) |= ", p.typ(part, bft), part.Offset-off)
+			p.assignmentExpression(f, part.AssignmentExpression, bft, exprValue, 0)
+			p.w("&%#x", uint64(1)<= exprMode(len(_exprMode_index)-1) {
+		return "exprMode(" + strconv.FormatInt(int64(i+1), 10) + ")"
+	}
+	return _exprMode_name[_exprMode_index[i]:_exprMode_index[i+1]]
+}
+func _() {
+	// An "invalid array index" compiler error signifies that the constant values have changed.
+	// Re-run the stringer command to generate them again.
+	var x [1]struct{}
+	_ = x[opNormal-0]
+	_ = x[opArray-1]
+	_ = x[opArrayParameter-2]
+	_ = x[opFunction-3]
+	_ = x[opUnion-4]
+	_ = x[opBitfield-5]
+	_ = x[opStruct-6]
+}
+
+const _opKind_name = "opNormalopArrayopArrayParameteropFunctionopUnionopBitfieldopStruct"
+
+var _opKind_index = [...]uint8{0, 8, 15, 31, 41, 48, 58, 66}
+
+func (i opKind) String() string {
+	if i < 0 || i >= opKind(len(_opKind_index)-1) {
+		return "opKind(" + strconv.FormatInt(int64(i), 10) + ")"
+	}
+	return _opKind_name[_opKind_index[i]:_opKind_index[i+1]]
+}
diff --git a/vendor/modernc.org/ccgo/v3/lib/unconvert.sh b/vendor/modernc.org/ccgo/v3/lib/unconvert.sh
new file mode 100644
index 00000000..af3b87f8
--- /dev/null
+++ b/vendor/modernc.org/ccgo/v3/lib/unconvert.sh
@@ -0,0 +1,4 @@
+until unconvert -fastmath . &> /dev/null
+do
+	unconvert -fastmath -apply . &> /dev/null
+done
diff --git a/vendor/modernc.org/ccgo/v3/lib/util.go b/vendor/modernc.org/ccgo/v3/lib/util.go
new file mode 100644
index 00000000..f11e439c
--- /dev/null
+++ b/vendor/modernc.org/ccgo/v3/lib/util.go
@@ -0,0 +1,458 @@
+// Copyright 2020 The CCGO Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// generator.go helpers
+
+package ccgo // import "modernc.org/ccgo/v3/lib"
+
+import (
+	"archive/tar"
+	"bufio"
+	"bytes"
+	"compress/gzip"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"runtime/debug"
+	"strings"
+)
+
+// CopyFile copies src to dest, preserving permissions and times where/when
+// possible. If canOverwrite is not nil, it is consulted whether a destination
+// file can be overwritten. If canOverwrite is nil then destination is
+// overwritten if permissions allow that, otherwise the function fails.
+//
+// Src and dst must be in the slash form.
+func CopyFile(dst, src string, canOverwrite func(fn string, fi os.FileInfo) bool) (n int64, rerr error) {
+	dst = filepath.FromSlash(dst)
+	dstDir := filepath.Dir(dst)
+	di, err := os.Stat(dstDir)
+	switch {
+	case err != nil:
+		if !os.IsNotExist(err) {
+			return 0, err
+		}
+
+		if err := os.MkdirAll(dstDir, 0770); err != nil {
+			return 0, err
+		}
+	case err == nil:
+		if !di.IsDir() {
+			return 0, fmt.Errorf("cannot create directory, file exists: %s", dst)
+		}
+	}
+
+	src = filepath.FromSlash(src)
+	si, err := os.Stat(src)
+	if err != nil {
+		return 0, err
+	}
+
+	if si.IsDir() {
+		return 0, fmt.Errorf("cannot copy a directory: %s", src)
+	}
+
+	di, err = os.Stat(dst)
+	switch {
+	case err != nil && !os.IsNotExist(err):
+		return 0, err
+	case err == nil:
+		if di.IsDir() {
+			return 0, fmt.Errorf("cannot overwite a directory: %s", dst)
+		}
+
+		if canOverwrite != nil && !canOverwrite(dst, di) {
+			return 0, fmt.Errorf("cannot overwite: %s", dst)
+		}
+	}
+
+	s, err := os.Open(src)
+	if err != nil {
+		return 0, err
+	}
+
+	defer s.Close()
+	r := bufio.NewReader(s)
+
+	d, err := os.Create(dst)
+
+	defer func() {
+		if err := d.Close(); err != nil && rerr == nil {
+			rerr = err
+			return
+		}
+
+		if err := os.Chmod(dst, si.Mode()); err != nil && rerr == nil {
+			rerr = err
+			return
+		}
+
+		if err := os.Chtimes(dst, si.ModTime(), si.ModTime()); err != nil && rerr == nil {
+			rerr = err
+			return
+		}
+	}()
+
+	w := bufio.NewWriter(d)
+
+	defer func() {
+		if err := w.Flush(); err != nil && rerr == nil {
+			rerr = err
+		}
+	}()
+
+	return io.Copy(w, r)
+}
+
+// MustCopyFile is like CopyFile but it executes Fatal(stackTrace, err) if it fails.
+func MustCopyFile(stackTrace bool, dst, src string, canOverwrite func(fn string, fi os.FileInfo) bool) int64 {
+	n, err := CopyFile(dst, src, canOverwrite)
+	if err != nil {
+		Fatal(stackTrace, err)
+	}
+
+	return n
+}
+
+// CopyDir recursively copies src to dest, preserving permissions and times
+// where/when possible. If canOverwrite is not nil, it is consulted whether a
+// destination file can be overwritten. If canOverwrite is nil then destination
+// is overwritten if permissions allow that, otherwise the function fails.
+//
+// Src and dst must be in the slash form.
+func CopyDir(dst, src string, canOverwrite func(fn string, fi os.FileInfo) bool) (files int, bytes int64, rerr error) {
+	dst = filepath.FromSlash(dst)
+	src = filepath.FromSlash(src)
+	si, err := os.Stat(src)
+	if err != nil {
+		return 0, 0, err
+	}
+
+	if !si.IsDir() {
+		return 0, 0, fmt.Errorf("cannot copy a file: %s", src)
+	}
+
+	return files, bytes, filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
+		if err != nil {
+			return err
+		}
+
+		rel, err := filepath.Rel(src, path)
+		if err != nil {
+			return err
+		}
+
+		if info.IsDir() {
+			return os.MkdirAll(filepath.Join(dst, rel), 0770)
+		}
+
+		n, err := CopyFile(filepath.Join(dst, rel), path, canOverwrite)
+		if err != nil {
+			return err
+		}
+
+		files++
+		bytes += n
+		return nil
+	})
+}
+
+// MustCopyDir is like CopyDir, but it executes Fatal(stackTrace, errĂº if it fails.
+func MustCopyDir(stackTrace bool, dst, src string, canOverwrite func(fn string, fi os.FileInfo) bool) (files int, bytes int64) {
+	file, bytes, err := CopyDir(dst, src, canOverwrite)
+	if err != nil {
+		Fatal(stackTrace, err)
+	}
+
+	return file, bytes
+}
+
+// UntarFile extracts a named tar.gz archive into dst. If canOverwrite is not
+// nil, it is consulted whether a destination file can be overwritten. If
+// canOverwrite is nil then destination is overwritten if permissions allow
+// that, otherwise the function fails.
+//
+// Src and dst must be in the slash form.
+func UntarFile(dst, src string, canOverwrite func(fn string, fi os.FileInfo) bool) error {
+	f, err := os.Open(filepath.FromSlash(src))
+	if err != nil {
+		return err
+	}
+
+	defer f.Close()
+
+	return Untar(dst, bufio.NewReader(f), canOverwrite)
+}
+
+// MustUntarFile is like UntarFile but it executes Fatal(stackTrace, err) if it fails.
+func MustUntarFile(stackTrace bool, dst, src string, canOverwrite func(fn string, fi os.FileInfo) bool) {
+	if err := UntarFile(dst, src, canOverwrite); err != nil {
+		Fatal(stackTrace, err)
+	}
+}
+
+// Untar extracts a tar.gz archive into dst. If canOverwrite is not nil, it is
+// consulted whether a destination file can be overwritten. If canOverwrite is
+// nil then destination is overwritten if permissions allow that, otherwise the
+// function fails.
+//
+// Dst must be in the slash form.
+func Untar(dst string, r io.Reader, canOverwrite func(fn string, fi os.FileInfo) bool) error {
+	dst = filepath.FromSlash(dst)
+	gr, err := gzip.NewReader(r)
+	if err != nil {
+		return err
+	}
+
+	tr := tar.NewReader(gr)
+	for {
+		hdr, err := tr.Next()
+		if err != nil {
+			if err != io.EOF {
+				return err
+			}
+
+			return nil
+		}
+
+		switch hdr.Typeflag {
+		case tar.TypeDir:
+			dir := filepath.Join(dst, hdr.Name)
+			if err = os.MkdirAll(dir, 0770); err != nil {
+				return err
+			}
+		case tar.TypeSymlink, tar.TypeXGlobalHeader:
+			// skip
+		case tar.TypeReg, tar.TypeRegA:
+			dir := filepath.Dir(filepath.Join(dst, hdr.Name))
+			if _, err := os.Stat(dir); err != nil {
+				if !os.IsNotExist(err) {
+					return err
+				}
+
+				if err = os.MkdirAll(dir, 0770); err != nil {
+					return err
+				}
+			}
+
+			fn := filepath.Join(dst, hdr.Name)
+			f, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY, os.FileMode(hdr.Mode))
+			if err != nil {
+				return err
+			}
+
+			w := bufio.NewWriter(f)
+			if _, err = io.Copy(w, tr); err != nil {
+				return err
+			}
+
+			if err := w.Flush(); err != nil {
+				return err
+			}
+
+			if err := f.Close(); err != nil {
+				return err
+			}
+
+			if err := os.Chtimes(fn, hdr.AccessTime, hdr.ModTime); err != nil {
+				return err
+			}
+		default:
+			return fmt.Errorf("unexpected tar header typeflag %#02x", hdr.Typeflag)
+		}
+	}
+
+}
+
+// MustUntar is like Untar but it executes Fatal(stackTrace, err) if it fails.
+func MustUntar(stackTrace bool, dst string, r io.Reader, canOverwrite func(fn string, fi os.FileInfo) bool) {
+	if err := Untar(dst, r, canOverwrite); err != nil {
+		Fatal(stackTrace, err)
+	}
+}
+
+// Fatalf prints a formatted message to os.Stderr and performs os.Exit(1). A
+// stack trace is added if stackTrace is true.
+func Fatalf(stackTrace bool, s string, args ...interface{}) {
+	if stackTrace {
+		fmt.Fprintf(os.Stderr, "%s\n", debug.Stack())
+	}
+	fmt.Fprintln(os.Stderr, strings.TrimSpace(fmt.Sprintf(s, args...)))
+	os.Exit(1)
+}
+
+// Fatal prints its argumenst to os.Stderr and performs os.Exit(1). A
+// stack trace is added if stackTrace is true.
+func Fatal(stackTrace bool, args ...interface{}) {
+	if stackTrace {
+		fmt.Fprintf(os.Stderr, "%s\n", debug.Stack())
+	}
+	fmt.Fprintln(os.Stderr, strings.TrimSpace(fmt.Sprint(args...)))
+	os.Exit(1)
+}
+
+// Mkdirs will create all paths. Paths must be in slash form.
+func Mkdirs(paths ...string) error {
+	for _, path := range paths {
+		path = filepath.FromSlash(path)
+		if err := os.MkdirAll(path, 0770); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// MustMkdirs is like Mkdir but if executes Fatal(stackTrace, err) if it fails.
+func MustMkdirs(stackTrace bool, paths ...string) {
+	if err := Mkdirs(paths...); err != nil {
+		Fatal(stackTrace, err)
+	}
+}
+
+// InDir executes f in dir. Dir must be in slash form.
+func InDir(dir string, f func() error) (err error) {
+	var cwd string
+	if cwd, err = os.Getwd(); err != nil {
+		return err
+	}
+
+	defer func() {
+		if err2 := os.Chdir(cwd); err2 != nil {
+			err = err2
+		}
+	}()
+
+	if err = os.Chdir(filepath.FromSlash(dir)); err != nil {
+		return err
+	}
+
+	return f()
+}
+
+// MustInDir is like InDir but it executes Fatal(stackTrace, err) if it fails.
+func MustInDir(stackTrace bool, dir string, f func() error) {
+	if err := InDir(dir, f); err != nil {
+		Fatal(stackTrace, err)
+	}
+}
+
+type echoWriter struct {
+	w bytes.Buffer
+}
+
+func (w *echoWriter) Write(b []byte) (int, error) {
+	os.Stdout.Write(b)
+	return w.w.Write(b)
+}
+
+// Shell echoes and executes cmd with args and returns the combined output if the command.
+func Shell(cmd string, args ...string) ([]byte, error) {
+	cmd, err := exec.LookPath(cmd)
+	if err != nil {
+		return nil, err
+	}
+
+	wd, err := AbsCwd()
+	if err != nil {
+		return nil, err
+	}
+
+	fmt.Printf("execute %s %q in %s\n", cmd, args, wd)
+	var b echoWriter
+	c := exec.Command(cmd, args...)
+	c.Stdout = &b
+	c.Stderr = &b
+	err = c.Run()
+	return b.w.Bytes(), err
+}
+
+// MustShell is like Shell but it executes Fatal(stackTrace, err) if it fails.
+func MustShell(stackTrace bool, cmd string, args ...string) []byte {
+	b, err := Shell(cmd, args...)
+	if err != nil {
+		Fatalf(stackTrace, "%v %s\noutput: %s\nerr: %s", cmd, args, b, err)
+	}
+
+	return b
+}
+
+// Compile executes Shell with cmd set to "ccgo".
+func Compile(args ...string) ([]byte, error) { return Shell("ccgo", args...) }
+
+// MustCompile is like Compile but if executes Fatal(stackTrace, err) if it fails.
+func MustCompile(stackTrace bool, args ...string) []byte {
+	return MustShell(stackTrace, "ccgo", args...)
+}
+
+// Run is like Compile, but executes in-process.
+func Run(args ...string) ([]byte, error) {
+	var b bytes.Buffer
+	t := NewTask(append([]string{"ccgo"}, args...), &b, &b)
+	err := t.Main()
+	return b.Bytes(), err
+}
+
+// MustRun is like Run but if executes Fatal(stackTrace, err) if it fails.
+func MustRun(stackTrace bool, args ...string) []byte {
+	var b bytes.Buffer
+	args = append([]string{"ccgo"}, args...)
+	t := NewTask(args, &b, &b)
+	if err := t.Main(); err != nil {
+		Fatalf(stackTrace, "%v\noutput: %s\nerr: %s", args, b.Bytes(), err)
+	}
+
+	return b.Bytes()
+}
+
+// AbsCwd returns the absolute working directory.
+func AbsCwd() (string, error) {
+	wd, err := os.Getwd()
+	if err != nil {
+		return "", err
+	}
+
+	if wd, err = filepath.Abs(wd); err != nil {
+		return "", err
+	}
+
+	return wd, nil
+}
+
+// MustAbsCwd is like AbsCwd but executes Fatal(stackTrace, err) if it fails.
+func MustAbsCwd(stackTrace bool) string {
+	s, err := AbsCwd()
+	if err != nil {
+		Fatal(stackTrace, err)
+	}
+
+	return s
+}
+
+// Env returns the value of environmental variable key of dflt otherwise.
+func Env(key, dflt string) string {
+	if s := os.Getenv(key); s != "" {
+		return s
+	}
+
+	return dflt
+}
+
+// MustTempDir is like ioutil.TempDir but executes Fatal(stackTrace, err) if it
+// fails. The returned path is absolute.
+func MustTempDir(stackTrace bool, dir, name string) string {
+	s, err := ioutil.TempDir(dir, name)
+	if err != nil {
+		Fatal(stackTrace, err)
+	}
+
+	if s, err = filepath.Abs(s); err != nil {
+		Fatal(stackTrace, err)
+	}
+
+	return s
+}
-- 
cgit v1.2.3