Home | History | Annotate | Download | only in gc
      1 // Copyright 2009 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package gc
      6 
      7 import (
      8 	"cmd/compile/internal/syntax"
      9 	"cmd/internal/obj"
     10 	"fmt"
     11 	"strings"
     12 )
     13 
     14 // lexlineno is the line number _after_ the most recently read rune.
     15 // In particular, it's advanced (or rewound) as newlines are read (or unread).
     16 var lexlineno int32
     17 
     18 // lineno is the line number at the start of the most recently lexed token.
     19 var lineno int32
     20 
     21 func isSpace(c rune) bool {
     22 	return c == ' ' || c == '\t' || c == '\n' || c == '\r'
     23 }
     24 
     25 func isQuoted(s string) bool {
     26 	return len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"'
     27 }
     28 
     29 func plan9quote(s string) string {
     30 	if s == "" {
     31 		return "''"
     32 	}
     33 	for _, c := range s {
     34 		if c <= ' ' || c == '\'' {
     35 			return "'" + strings.Replace(s, "'", "''", -1) + "'"
     36 		}
     37 	}
     38 	return s
     39 }
     40 
     41 type Pragma syntax.Pragma
     42 
     43 const (
     44 	// Func pragmas.
     45 	Nointerface    Pragma = 1 << iota
     46 	Noescape              // func parameters don't escape
     47 	Norace                // func must not have race detector annotations
     48 	Nosplit               // func should not execute on separate stack
     49 	Noinline              // func should not be inlined
     50 	CgoUnsafeArgs         // treat a pointer to one arg as a pointer to them all
     51 	UintptrEscapes        // pointers converted to uintptr escape
     52 
     53 	// Runtime-only func pragmas.
     54 	// See ../../../../runtime/README.md for detailed descriptions.
     55 	Systemstack        // func must run on system stack
     56 	Nowritebarrier     // emit compiler error instead of write barrier
     57 	Nowritebarrierrec  // error on write barrier in this or recursive callees
     58 	Yeswritebarrierrec // cancels Nowritebarrierrec in this function and callees
     59 
     60 	// Runtime-only type pragmas
     61 	NotInHeap // values of this type must not be heap allocated
     62 )
     63 
     64 func pragmaValue(verb string) Pragma {
     65 	switch verb {
     66 	case "go:nointerface":
     67 		if obj.Fieldtrack_enabled != 0 {
     68 			return Nointerface
     69 		}
     70 	case "go:noescape":
     71 		return Noescape
     72 	case "go:norace":
     73 		return Norace
     74 	case "go:nosplit":
     75 		return Nosplit
     76 	case "go:noinline":
     77 		return Noinline
     78 	case "go:systemstack":
     79 		if !compiling_runtime {
     80 			yyerror("//go:systemstack only allowed in runtime")
     81 		}
     82 		return Systemstack
     83 	case "go:nowritebarrier":
     84 		if !compiling_runtime {
     85 			yyerror("//go:nowritebarrier only allowed in runtime")
     86 		}
     87 		return Nowritebarrier
     88 	case "go:nowritebarrierrec":
     89 		if !compiling_runtime {
     90 			yyerror("//go:nowritebarrierrec only allowed in runtime")
     91 		}
     92 		return Nowritebarrierrec | Nowritebarrier // implies Nowritebarrier
     93 	case "go:yeswritebarrierrec":
     94 		if !compiling_runtime {
     95 			yyerror("//go:yeswritebarrierrec only allowed in runtime")
     96 		}
     97 		return Yeswritebarrierrec
     98 	case "go:cgo_unsafe_args":
     99 		return CgoUnsafeArgs
    100 	case "go:uintptrescapes":
    101 		// For the next function declared in the file
    102 		// any uintptr arguments may be pointer values
    103 		// converted to uintptr. This directive
    104 		// ensures that the referenced allocated
    105 		// object, if any, is retained and not moved
    106 		// until the call completes, even though from
    107 		// the types alone it would appear that the
    108 		// object is no longer needed during the
    109 		// call. The conversion to uintptr must appear
    110 		// in the argument list.
    111 		// Used in syscall/dll_windows.go.
    112 		return UintptrEscapes
    113 	case "go:notinheap":
    114 		return NotInHeap
    115 	}
    116 	return 0
    117 }
    118 
    119 var internedStrings = map[string]string{}
    120 
    121 func internString(b []byte) string {
    122 	s, ok := internedStrings[string(b)] // string(b) here doesn't allocate
    123 	if !ok {
    124 		s = string(b)
    125 		internedStrings[s] = s
    126 	}
    127 	return s
    128 }
    129 
    130 func pragcgo(text string) string {
    131 	f := pragmaFields(text)
    132 
    133 	verb := f[0][3:] // skip "go:"
    134 	switch verb {
    135 	case "cgo_export_static", "cgo_export_dynamic":
    136 		switch {
    137 		case len(f) == 2 && !isQuoted(f[1]):
    138 			local := plan9quote(f[1])
    139 			return fmt.Sprintln(verb, local)
    140 
    141 		case len(f) == 3 && !isQuoted(f[1]) && !isQuoted(f[2]):
    142 			local := plan9quote(f[1])
    143 			remote := plan9quote(f[2])
    144 			return fmt.Sprintln(verb, local, remote)
    145 
    146 		default:
    147 			yyerror(`usage: //go:%s local [remote]`, verb)
    148 		}
    149 	case "cgo_import_dynamic":
    150 		switch {
    151 		case len(f) == 2 && !isQuoted(f[1]):
    152 			local := plan9quote(f[1])
    153 			return fmt.Sprintln(verb, local)
    154 
    155 		case len(f) == 3 && !isQuoted(f[1]) && !isQuoted(f[2]):
    156 			local := plan9quote(f[1])
    157 			remote := plan9quote(f[2])
    158 			return fmt.Sprintln(verb, local, remote)
    159 
    160 		case len(f) == 4 && !isQuoted(f[1]) && !isQuoted(f[2]) && isQuoted(f[3]):
    161 			local := plan9quote(f[1])
    162 			remote := plan9quote(f[2])
    163 			library := plan9quote(strings.Trim(f[3], `"`))
    164 			return fmt.Sprintln(verb, local, remote, library)
    165 
    166 		default:
    167 			yyerror(`usage: //go:cgo_import_dynamic local [remote ["library"]]`)
    168 		}
    169 	case "cgo_import_static":
    170 		switch {
    171 		case len(f) == 2 && !isQuoted(f[1]):
    172 			local := plan9quote(f[1])
    173 			return fmt.Sprintln(verb, local)
    174 
    175 		default:
    176 			yyerror(`usage: //go:cgo_import_static local`)
    177 		}
    178 	case "cgo_dynamic_linker":
    179 		switch {
    180 		case len(f) == 2 && isQuoted(f[1]):
    181 			path := plan9quote(strings.Trim(f[1], `"`))
    182 			return fmt.Sprintln(verb, path)
    183 
    184 		default:
    185 			yyerror(`usage: //go:cgo_dynamic_linker "path"`)
    186 		}
    187 	case "cgo_ldflag":
    188 		switch {
    189 		case len(f) == 2 && isQuoted(f[1]):
    190 			arg := plan9quote(strings.Trim(f[1], `"`))
    191 			return fmt.Sprintln(verb, arg)
    192 
    193 		default:
    194 			yyerror(`usage: //go:cgo_ldflag "arg"`)
    195 		}
    196 	}
    197 	return ""
    198 }
    199 
    200 // pragmaFields is similar to strings.FieldsFunc(s, isSpace)
    201 // but does not split when inside double quoted regions and always
    202 // splits before the start and after the end of a double quoted region.
    203 // pragmaFields does not recognize escaped quotes. If a quote in s is not
    204 // closed the part after the opening quote will not be returned as a field.
    205 func pragmaFields(s string) []string {
    206 	var a []string
    207 	inQuote := false
    208 	fieldStart := -1 // Set to -1 when looking for start of field.
    209 	for i, c := range s {
    210 		switch {
    211 		case c == '"':
    212 			if inQuote {
    213 				inQuote = false
    214 				a = append(a, s[fieldStart:i+1])
    215 				fieldStart = -1
    216 			} else {
    217 				inQuote = true
    218 				if fieldStart >= 0 {
    219 					a = append(a, s[fieldStart:i])
    220 				}
    221 				fieldStart = i
    222 			}
    223 		case !inQuote && isSpace(c):
    224 			if fieldStart >= 0 {
    225 				a = append(a, s[fieldStart:i])
    226 				fieldStart = -1
    227 			}
    228 		default:
    229 			if fieldStart == -1 {
    230 				fieldStart = i
    231 			}
    232 		}
    233 	}
    234 	if !inQuote && fieldStart >= 0 { // Last field might end at the end of the string.
    235 		a = append(a, s[fieldStart:])
    236 	}
    237 	return a
    238 }
    239