github.com/aykevl/tinygo@v0.5.0/builtins.go (about) 1 package main 2 3 import ( 4 "errors" 5 "io" 6 "io/ioutil" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "strings" 11 "time" 12 13 "github.com/blakesmith/ar" 14 ) 15 16 // These are the GENERIC_SOURCES according to CMakeList.txt. 17 var genericBuiltins = []string{ 18 "absvdi2.c", 19 "absvsi2.c", 20 "absvti2.c", 21 "adddf3.c", 22 "addsf3.c", 23 "addtf3.c", 24 "addvdi3.c", 25 "addvsi3.c", 26 "addvti3.c", 27 "apple_versioning.c", 28 "ashldi3.c", 29 "ashlti3.c", 30 "ashrdi3.c", 31 "ashrti3.c", 32 "bswapdi2.c", 33 "bswapsi2.c", 34 "clzdi2.c", 35 "clzsi2.c", 36 "clzti2.c", 37 "cmpdi2.c", 38 "cmpti2.c", 39 "comparedf2.c", 40 "comparesf2.c", 41 "ctzdi2.c", 42 "ctzsi2.c", 43 "ctzti2.c", 44 "divdc3.c", 45 "divdf3.c", 46 "divdi3.c", 47 "divmoddi4.c", 48 "divmodsi4.c", 49 "divsc3.c", 50 "divsf3.c", 51 "divsi3.c", 52 "divtc3.c", 53 "divti3.c", 54 "divtf3.c", 55 "extendsfdf2.c", 56 "extendhfsf2.c", 57 "ffsdi2.c", 58 "ffssi2.c", 59 "ffsti2.c", 60 "fixdfdi.c", 61 "fixdfsi.c", 62 "fixdfti.c", 63 "fixsfdi.c", 64 "fixsfsi.c", 65 "fixsfti.c", 66 "fixunsdfdi.c", 67 "fixunsdfsi.c", 68 "fixunsdfti.c", 69 "fixunssfdi.c", 70 "fixunssfsi.c", 71 "fixunssfti.c", 72 "floatdidf.c", 73 "floatdisf.c", 74 "floatsidf.c", 75 "floatsisf.c", 76 "floattidf.c", 77 "floattisf.c", 78 "floatundidf.c", 79 "floatundisf.c", 80 "floatunsidf.c", 81 "floatunsisf.c", 82 "floatuntidf.c", 83 "floatuntisf.c", 84 //"int_util.c", 85 "lshrdi3.c", 86 "lshrti3.c", 87 "moddi3.c", 88 "modsi3.c", 89 "modti3.c", 90 "muldc3.c", 91 "muldf3.c", 92 "muldi3.c", 93 "mulodi4.c", 94 "mulosi4.c", 95 "muloti4.c", 96 "mulsc3.c", 97 "mulsf3.c", 98 "multi3.c", 99 "multf3.c", 100 "mulvdi3.c", 101 "mulvsi3.c", 102 "mulvti3.c", 103 "negdf2.c", 104 "negdi2.c", 105 "negsf2.c", 106 "negti2.c", 107 "negvdi2.c", 108 "negvsi2.c", 109 "negvti2.c", 110 "os_version_check.c", 111 "paritydi2.c", 112 "paritysi2.c", 113 "parityti2.c", 114 "popcountdi2.c", 115 "popcountsi2.c", 116 "popcountti2.c", 117 "powidf2.c", 118 "powisf2.c", 119 "powitf2.c", 120 "subdf3.c", 121 "subsf3.c", 122 "subvdi3.c", 123 "subvsi3.c", 124 "subvti3.c", 125 "subtf3.c", 126 "trampoline_setup.c", 127 "truncdfhf2.c", 128 "truncdfsf2.c", 129 "truncsfhf2.c", 130 "ucmpdi2.c", 131 "ucmpti2.c", 132 "udivdi3.c", 133 "udivmoddi4.c", 134 "udivmodsi4.c", 135 "udivmodti4.c", 136 "udivsi3.c", 137 "udivti3.c", 138 "umoddi3.c", 139 "umodsi3.c", 140 "umodti3.c", 141 } 142 143 var aeabiBuiltins = []string{ 144 "arm/aeabi_cdcmp.S", 145 "arm/aeabi_cdcmpeq_check_nan.c", 146 "arm/aeabi_cfcmp.S", 147 "arm/aeabi_cfcmpeq_check_nan.c", 148 "arm/aeabi_dcmp.S", 149 "arm/aeabi_div0.c", 150 "arm/aeabi_drsub.c", 151 "arm/aeabi_fcmp.S", 152 "arm/aeabi_frsub.c", 153 "arm/aeabi_idivmod.S", 154 "arm/aeabi_ldivmod.S", 155 "arm/aeabi_memcmp.S", 156 "arm/aeabi_memcpy.S", 157 "arm/aeabi_memmove.S", 158 "arm/aeabi_memset.S", 159 "arm/aeabi_uidivmod.S", 160 "arm/aeabi_uldivmod.S", 161 } 162 163 func builtinFiles(target string) []string { 164 builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins 165 if strings.HasPrefix(target, "arm") { 166 builtins = append(builtins, aeabiBuiltins...) 167 } 168 return builtins 169 } 170 171 // builtinsDir returns the directory where the sources for compiler-rt are kept. 172 func builtinsDir() string { 173 return filepath.Join(sourceDir(), "lib", "compiler-rt", "lib", "builtins") 174 } 175 176 // Get the builtins archive, possibly generating it as needed. 177 func loadBuiltins(target string) (path string, err error) { 178 // Try to load a precompiled compiler-rt library. 179 precompiledPath := filepath.Join(sourceDir(), "pkg", target, "compiler-rt.a") 180 if _, err := os.Stat(precompiledPath); err == nil { 181 // Found a precompiled compiler-rt for this OS/architecture. Return the 182 // path directly. 183 return precompiledPath, nil 184 } 185 186 outfile := "librt-" + target + ".a" 187 builtinsDir := builtinsDir() 188 189 builtins := builtinFiles(target) 190 srcs := make([]string, len(builtins)) 191 for i, name := range builtins { 192 srcs[i] = filepath.Join(builtinsDir, name) 193 } 194 195 if path, err := cacheLoad(outfile, commands["clang"], srcs); path != "" || err != nil { 196 return path, err 197 } 198 199 var cachepath string 200 err = compileBuiltins(target, func(path string) error { 201 path, err := cacheStore(path, outfile, commands["clang"], srcs) 202 cachepath = path 203 return err 204 }) 205 return cachepath, err 206 } 207 208 // compileBuiltins compiles builtins from compiler-rt into a static library. 209 // When it succeeds, it will call the callback with the resulting path. The path 210 // will be removed after callback returns. If callback returns an error, this is 211 // passed through to the return value of this function. 212 func compileBuiltins(target string, callback func(path string) error) error { 213 builtinsDir := builtinsDir() 214 215 builtins := builtinFiles(target) 216 srcs := make([]string, len(builtins)) 217 for i, name := range builtins { 218 srcs[i] = filepath.Join(builtinsDir, name) 219 } 220 221 dirPrefix := "tinygo-builtins" 222 remapDir := filepath.Join(os.TempDir(), dirPrefix) 223 dir, err := ioutil.TempDir(os.TempDir(), dirPrefix) 224 if err != nil { 225 return err 226 } 227 defer os.RemoveAll(dir) 228 229 // Compile all builtins. 230 // TODO: use builtins optimized for a given target if available. 231 objs := make([]string, 0, len(builtins)) 232 for _, name := range builtins { 233 objname := name 234 if strings.LastIndexByte(objname, '/') >= 0 { 235 objname = objname[strings.LastIndexByte(objname, '/'):] 236 } 237 objpath := filepath.Join(dir, objname+".o") 238 objs = append(objs, objpath) 239 srcpath := filepath.Join(builtinsDir, name) 240 // Note: -fdebug-prefix-map is necessary to make the output archive 241 // reproducible. Otherwise the temporary directory is stored in the 242 // archive itself, which varies each run. 243 cmd := exec.Command(commands["clang"], "-c", "-Oz", "-g", "-Werror", "-Wall", "-std=c11", "-fshort-enums", "-nostdlibinc", "-ffunction-sections", "-fdata-sections", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir, "-o", objpath, srcpath) 244 cmd.Stdout = os.Stdout 245 cmd.Stderr = os.Stderr 246 cmd.Dir = dir 247 err = cmd.Run() 248 if err != nil { 249 return &commandError{"failed to build", srcpath, err} 250 } 251 } 252 253 // Put all builtins in an archive to link as a static library. 254 // Note: this does not create a symbol index, but ld.lld doesn't seem to 255 // care. 256 arpath := filepath.Join(dir, "librt.a") 257 arfile, err := os.Create(arpath) 258 if err != nil { 259 return err 260 } 261 defer arfile.Close() 262 arwriter := ar.NewWriter(arfile) 263 err = arwriter.WriteGlobalHeader() 264 if err != nil { 265 return &os.PathError{"write ar header", arpath, err} 266 } 267 for _, objpath := range objs { 268 name := filepath.Base(objpath) 269 objfile, err := os.Open(objpath) 270 if err != nil { 271 return err 272 } 273 defer objfile.Close() 274 st, err := objfile.Stat() 275 if err != nil { 276 return err 277 } 278 arwriter.WriteHeader(&ar.Header{ 279 Name: name, 280 ModTime: time.Unix(0, 0), 281 Uid: 0, 282 Gid: 0, 283 Mode: 0644, 284 Size: st.Size(), 285 }) 286 n, err := io.Copy(arwriter, objfile) 287 if err != nil { 288 return err 289 } 290 if n != st.Size() { 291 return errors.New("file modified during ar creation: " + arpath) 292 } 293 } 294 295 // Give the caller the resulting file. The callback must copy the file, 296 // because after it returns the temporary directory will be removed. 297 return callback(arpath) 298 }