wasmobj.go 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185
  1. // Copyright 2018 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. package wasm
  5. import (
  6. "bytes"
  7. "github.com/twitchyliquid64/golang-asm/obj"
  8. "github.com/twitchyliquid64/golang-asm/objabi"
  9. "github.com/twitchyliquid64/golang-asm/sys"
  10. "encoding/binary"
  11. "fmt"
  12. "io"
  13. "math"
  14. )
  15. var Register = map[string]int16{
  16. "SP": REG_SP,
  17. "CTXT": REG_CTXT,
  18. "g": REG_g,
  19. "RET0": REG_RET0,
  20. "RET1": REG_RET1,
  21. "RET2": REG_RET2,
  22. "RET3": REG_RET3,
  23. "PAUSE": REG_PAUSE,
  24. "R0": REG_R0,
  25. "R1": REG_R1,
  26. "R2": REG_R2,
  27. "R3": REG_R3,
  28. "R4": REG_R4,
  29. "R5": REG_R5,
  30. "R6": REG_R6,
  31. "R7": REG_R7,
  32. "R8": REG_R8,
  33. "R9": REG_R9,
  34. "R10": REG_R10,
  35. "R11": REG_R11,
  36. "R12": REG_R12,
  37. "R13": REG_R13,
  38. "R14": REG_R14,
  39. "R15": REG_R15,
  40. "F0": REG_F0,
  41. "F1": REG_F1,
  42. "F2": REG_F2,
  43. "F3": REG_F3,
  44. "F4": REG_F4,
  45. "F5": REG_F5,
  46. "F6": REG_F6,
  47. "F7": REG_F7,
  48. "F8": REG_F8,
  49. "F9": REG_F9,
  50. "F10": REG_F10,
  51. "F11": REG_F11,
  52. "F12": REG_F12,
  53. "F13": REG_F13,
  54. "F14": REG_F14,
  55. "F15": REG_F15,
  56. "F16": REG_F16,
  57. "F17": REG_F17,
  58. "F18": REG_F18,
  59. "F19": REG_F19,
  60. "F20": REG_F20,
  61. "F21": REG_F21,
  62. "F22": REG_F22,
  63. "F23": REG_F23,
  64. "F24": REG_F24,
  65. "F25": REG_F25,
  66. "F26": REG_F26,
  67. "F27": REG_F27,
  68. "F28": REG_F28,
  69. "F29": REG_F29,
  70. "F30": REG_F30,
  71. "F31": REG_F31,
  72. "PC_B": REG_PC_B,
  73. }
  74. var registerNames []string
  75. func init() {
  76. obj.RegisterRegister(MINREG, MAXREG, rconv)
  77. obj.RegisterOpcode(obj.ABaseWasm, Anames)
  78. registerNames = make([]string, MAXREG-MINREG)
  79. for name, reg := range Register {
  80. registerNames[reg-MINREG] = name
  81. }
  82. }
  83. func rconv(r int) string {
  84. return registerNames[r-MINREG]
  85. }
  86. var unaryDst = map[obj.As]bool{
  87. ASet: true,
  88. ATee: true,
  89. ACall: true,
  90. ACallIndirect: true,
  91. ACallImport: true,
  92. ABr: true,
  93. ABrIf: true,
  94. ABrTable: true,
  95. AI32Store: true,
  96. AI64Store: true,
  97. AF32Store: true,
  98. AF64Store: true,
  99. AI32Store8: true,
  100. AI32Store16: true,
  101. AI64Store8: true,
  102. AI64Store16: true,
  103. AI64Store32: true,
  104. ACALLNORESUME: true,
  105. }
  106. var Linkwasm = obj.LinkArch{
  107. Arch: sys.ArchWasm,
  108. Init: instinit,
  109. Preprocess: preprocess,
  110. Assemble: assemble,
  111. UnaryDst: unaryDst,
  112. }
  113. var (
  114. morestack *obj.LSym
  115. morestackNoCtxt *obj.LSym
  116. gcWriteBarrier *obj.LSym
  117. sigpanic *obj.LSym
  118. sigpanic0 *obj.LSym
  119. deferreturn *obj.LSym
  120. jmpdefer *obj.LSym
  121. )
  122. const (
  123. /* mark flags */
  124. WasmImport = 1 << 0
  125. )
  126. func instinit(ctxt *obj.Link) {
  127. morestack = ctxt.Lookup("runtime.morestack")
  128. morestackNoCtxt = ctxt.Lookup("runtime.morestack_noctxt")
  129. gcWriteBarrier = ctxt.Lookup("runtime.gcWriteBarrier")
  130. sigpanic = ctxt.LookupABI("runtime.sigpanic", obj.ABIInternal)
  131. sigpanic0 = ctxt.LookupABI("runtime.sigpanic", 0) // sigpanic called from assembly, which has ABI0
  132. deferreturn = ctxt.LookupABI("runtime.deferreturn", obj.ABIInternal)
  133. // jmpdefer is defined in assembly as ABI0, but what we're
  134. // looking for is the *call* to jmpdefer from the Go function
  135. // deferreturn, so we're looking for the ABIInternal version
  136. // of jmpdefer that's called by Go.
  137. jmpdefer = ctxt.LookupABI(`"".jmpdefer`, obj.ABIInternal)
  138. }
  139. func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
  140. appendp := func(p *obj.Prog, as obj.As, args ...obj.Addr) *obj.Prog {
  141. if p.As != obj.ANOP {
  142. p2 := obj.Appendp(p, newprog)
  143. p2.Pc = p.Pc
  144. p = p2
  145. }
  146. p.As = as
  147. switch len(args) {
  148. case 0:
  149. p.From = obj.Addr{}
  150. p.To = obj.Addr{}
  151. case 1:
  152. if unaryDst[as] {
  153. p.From = obj.Addr{}
  154. p.To = args[0]
  155. } else {
  156. p.From = args[0]
  157. p.To = obj.Addr{}
  158. }
  159. case 2:
  160. p.From = args[0]
  161. p.To = args[1]
  162. default:
  163. panic("bad args")
  164. }
  165. return p
  166. }
  167. framesize := s.Func.Text.To.Offset
  168. if framesize < 0 {
  169. panic("bad framesize")
  170. }
  171. s.Func.Args = s.Func.Text.To.Val.(int32)
  172. s.Func.Locals = int32(framesize)
  173. if s.Func.Text.From.Sym.Wrapper() {
  174. // if g._panic != nil && g._panic.argp == FP {
  175. // g._panic.argp = bottom-of-frame
  176. // }
  177. //
  178. // MOVD g_panic(g), R0
  179. // Get R0
  180. // I64Eqz
  181. // Not
  182. // If
  183. // Get SP
  184. // I64ExtendI32U
  185. // I64Const $framesize+8
  186. // I64Add
  187. // I64Load panic_argp(R0)
  188. // I64Eq
  189. // If
  190. // MOVD SP, panic_argp(R0)
  191. // End
  192. // End
  193. gpanic := obj.Addr{
  194. Type: obj.TYPE_MEM,
  195. Reg: REGG,
  196. Offset: 4 * 8, // g_panic
  197. }
  198. panicargp := obj.Addr{
  199. Type: obj.TYPE_MEM,
  200. Reg: REG_R0,
  201. Offset: 0, // panic.argp
  202. }
  203. p := s.Func.Text
  204. p = appendp(p, AMOVD, gpanic, regAddr(REG_R0))
  205. p = appendp(p, AGet, regAddr(REG_R0))
  206. p = appendp(p, AI64Eqz)
  207. p = appendp(p, ANot)
  208. p = appendp(p, AIf)
  209. p = appendp(p, AGet, regAddr(REG_SP))
  210. p = appendp(p, AI64ExtendI32U)
  211. p = appendp(p, AI64Const, constAddr(framesize+8))
  212. p = appendp(p, AI64Add)
  213. p = appendp(p, AI64Load, panicargp)
  214. p = appendp(p, AI64Eq)
  215. p = appendp(p, AIf)
  216. p = appendp(p, AMOVD, regAddr(REG_SP), panicargp)
  217. p = appendp(p, AEnd)
  218. p = appendp(p, AEnd)
  219. }
  220. if framesize > 0 {
  221. p := s.Func.Text
  222. p = appendp(p, AGet, regAddr(REG_SP))
  223. p = appendp(p, AI32Const, constAddr(framesize))
  224. p = appendp(p, AI32Sub)
  225. p = appendp(p, ASet, regAddr(REG_SP))
  226. p.Spadj = int32(framesize)
  227. }
  228. // Introduce resume points for CALL instructions
  229. // and collect other explicit resume points.
  230. numResumePoints := 0
  231. explicitBlockDepth := 0
  232. pc := int64(0) // pc is only incremented when necessary, this avoids bloat of the BrTable instruction
  233. var tableIdxs []uint64
  234. tablePC := int64(0)
  235. base := ctxt.PosTable.Pos(s.Func.Text.Pos).Base()
  236. for p := s.Func.Text; p != nil; p = p.Link {
  237. prevBase := base
  238. base = ctxt.PosTable.Pos(p.Pos).Base()
  239. switch p.As {
  240. case ABlock, ALoop, AIf:
  241. explicitBlockDepth++
  242. case AEnd:
  243. if explicitBlockDepth == 0 {
  244. panic("End without block")
  245. }
  246. explicitBlockDepth--
  247. case ARESUMEPOINT:
  248. if explicitBlockDepth != 0 {
  249. panic("RESUME can only be used on toplevel")
  250. }
  251. p.As = AEnd
  252. for tablePC <= pc {
  253. tableIdxs = append(tableIdxs, uint64(numResumePoints))
  254. tablePC++
  255. }
  256. numResumePoints++
  257. pc++
  258. case obj.ACALL:
  259. if explicitBlockDepth != 0 {
  260. panic("CALL can only be used on toplevel, try CALLNORESUME instead")
  261. }
  262. appendp(p, ARESUMEPOINT)
  263. }
  264. p.Pc = pc
  265. // Increase pc whenever some pc-value table needs a new entry. Don't increase it
  266. // more often to avoid bloat of the BrTable instruction.
  267. // The "base != prevBase" condition detects inlined instructions. They are an
  268. // implicit call, so entering and leaving this section affects the stack trace.
  269. if p.As == ACALLNORESUME || p.As == obj.ANOP || p.As == ANop || p.Spadj != 0 || base != prevBase {
  270. pc++
  271. if p.To.Sym == sigpanic {
  272. // The panic stack trace expects the PC at the call of sigpanic,
  273. // not the next one. However, runtime.Caller subtracts 1 from the
  274. // PC. To make both PC and PC-1 work (have the same line number),
  275. // we advance the PC by 2 at sigpanic.
  276. pc++
  277. }
  278. }
  279. }
  280. tableIdxs = append(tableIdxs, uint64(numResumePoints))
  281. s.Size = pc + 1
  282. if !s.Func.Text.From.Sym.NoSplit() {
  283. p := s.Func.Text
  284. if framesize <= objabi.StackSmall {
  285. // small stack: SP <= stackguard
  286. // Get SP
  287. // Get g
  288. // I32WrapI64
  289. // I32Load $stackguard0
  290. // I32GtU
  291. p = appendp(p, AGet, regAddr(REG_SP))
  292. p = appendp(p, AGet, regAddr(REGG))
  293. p = appendp(p, AI32WrapI64)
  294. p = appendp(p, AI32Load, constAddr(2*int64(ctxt.Arch.PtrSize))) // G.stackguard0
  295. p = appendp(p, AI32LeU)
  296. } else {
  297. // large stack: SP-framesize <= stackguard-StackSmall
  298. // SP <= stackguard+(framesize-StackSmall)
  299. // Get SP
  300. // Get g
  301. // I32WrapI64
  302. // I32Load $stackguard0
  303. // I32Const $(framesize-StackSmall)
  304. // I32Add
  305. // I32GtU
  306. p = appendp(p, AGet, regAddr(REG_SP))
  307. p = appendp(p, AGet, regAddr(REGG))
  308. p = appendp(p, AI32WrapI64)
  309. p = appendp(p, AI32Load, constAddr(2*int64(ctxt.Arch.PtrSize))) // G.stackguard0
  310. p = appendp(p, AI32Const, constAddr(int64(framesize)-objabi.StackSmall))
  311. p = appendp(p, AI32Add)
  312. p = appendp(p, AI32LeU)
  313. }
  314. // TODO(neelance): handle wraparound case
  315. p = appendp(p, AIf)
  316. p = appendp(p, obj.ACALL, constAddr(0))
  317. if s.Func.Text.From.Sym.NeedCtxt() {
  318. p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: morestack}
  319. } else {
  320. p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: morestackNoCtxt}
  321. }
  322. p = appendp(p, AEnd)
  323. }
  324. // record the branches targeting the entry loop and the unwind exit,
  325. // their targets with be filled in later
  326. var entryPointLoopBranches []*obj.Prog
  327. var unwindExitBranches []*obj.Prog
  328. currentDepth := 0
  329. for p := s.Func.Text; p != nil; p = p.Link {
  330. switch p.As {
  331. case ABlock, ALoop, AIf:
  332. currentDepth++
  333. case AEnd:
  334. currentDepth--
  335. }
  336. switch p.As {
  337. case obj.AJMP:
  338. jmp := *p
  339. p.As = obj.ANOP
  340. if jmp.To.Type == obj.TYPE_BRANCH {
  341. // jump to basic block
  342. p = appendp(p, AI32Const, constAddr(jmp.To.Val.(*obj.Prog).Pc))
  343. p = appendp(p, ASet, regAddr(REG_PC_B)) // write next basic block to PC_B
  344. p = appendp(p, ABr) // jump to beginning of entryPointLoop
  345. entryPointLoopBranches = append(entryPointLoopBranches, p)
  346. break
  347. }
  348. // low-level WebAssembly call to function
  349. switch jmp.To.Type {
  350. case obj.TYPE_MEM:
  351. if !notUsePC_B[jmp.To.Sym.Name] {
  352. // Set PC_B parameter to function entry.
  353. p = appendp(p, AI32Const, constAddr(0))
  354. }
  355. p = appendp(p, ACall, jmp.To)
  356. case obj.TYPE_NONE:
  357. // (target PC is on stack)
  358. p = appendp(p, AI32WrapI64)
  359. p = appendp(p, AI32Const, constAddr(16)) // only needs PC_F bits (16-31), PC_B bits (0-15) are zero
  360. p = appendp(p, AI32ShrU)
  361. // Set PC_B parameter to function entry.
  362. // We need to push this before pushing the target PC_F,
  363. // so temporarily pop PC_F, using our REG_PC_B as a
  364. // scratch register, and push it back after pushing 0.
  365. p = appendp(p, ASet, regAddr(REG_PC_B))
  366. p = appendp(p, AI32Const, constAddr(0))
  367. p = appendp(p, AGet, regAddr(REG_PC_B))
  368. p = appendp(p, ACallIndirect)
  369. default:
  370. panic("bad target for JMP")
  371. }
  372. p = appendp(p, AReturn)
  373. case obj.ACALL, ACALLNORESUME:
  374. call := *p
  375. p.As = obj.ANOP
  376. pcAfterCall := call.Link.Pc
  377. if call.To.Sym == sigpanic {
  378. pcAfterCall-- // sigpanic expects to be called without advancing the pc
  379. }
  380. // jmpdefer manipulates the return address on the stack so deferreturn gets called repeatedly.
  381. // Model this in WebAssembly with a loop.
  382. if call.To.Sym == deferreturn {
  383. p = appendp(p, ALoop)
  384. }
  385. // SP -= 8
  386. p = appendp(p, AGet, regAddr(REG_SP))
  387. p = appendp(p, AI32Const, constAddr(8))
  388. p = appendp(p, AI32Sub)
  389. p = appendp(p, ASet, regAddr(REG_SP))
  390. // write return address to Go stack
  391. p = appendp(p, AGet, regAddr(REG_SP))
  392. p = appendp(p, AI64Const, obj.Addr{
  393. Type: obj.TYPE_ADDR,
  394. Name: obj.NAME_EXTERN,
  395. Sym: s, // PC_F
  396. Offset: pcAfterCall, // PC_B
  397. })
  398. p = appendp(p, AI64Store, constAddr(0))
  399. // low-level WebAssembly call to function
  400. switch call.To.Type {
  401. case obj.TYPE_MEM:
  402. if !notUsePC_B[call.To.Sym.Name] {
  403. // Set PC_B parameter to function entry.
  404. p = appendp(p, AI32Const, constAddr(0))
  405. }
  406. p = appendp(p, ACall, call.To)
  407. case obj.TYPE_NONE:
  408. // (target PC is on stack)
  409. p = appendp(p, AI32WrapI64)
  410. p = appendp(p, AI32Const, constAddr(16)) // only needs PC_F bits (16-31), PC_B bits (0-15) are zero
  411. p = appendp(p, AI32ShrU)
  412. // Set PC_B parameter to function entry.
  413. // We need to push this before pushing the target PC_F,
  414. // so temporarily pop PC_F, using our PC_B as a
  415. // scratch register, and push it back after pushing 0.
  416. p = appendp(p, ASet, regAddr(REG_PC_B))
  417. p = appendp(p, AI32Const, constAddr(0))
  418. p = appendp(p, AGet, regAddr(REG_PC_B))
  419. p = appendp(p, ACallIndirect)
  420. default:
  421. panic("bad target for CALL")
  422. }
  423. // gcWriteBarrier has no return value, it never unwinds the stack
  424. if call.To.Sym == gcWriteBarrier {
  425. break
  426. }
  427. // jmpdefer removes the frame of deferreturn from the Go stack.
  428. // However, its WebAssembly function still returns normally,
  429. // so we need to return from deferreturn without removing its
  430. // stack frame (no RET), because the frame is already gone.
  431. if call.To.Sym == jmpdefer {
  432. p = appendp(p, AReturn)
  433. break
  434. }
  435. // return value of call is on the top of the stack, indicating whether to unwind the WebAssembly stack
  436. if call.As == ACALLNORESUME && call.To.Sym != sigpanic && call.To.Sym != sigpanic0 { // sigpanic unwinds the stack, but it never resumes
  437. // trying to unwind WebAssembly stack but call has no resume point, terminate with error
  438. p = appendp(p, AIf)
  439. p = appendp(p, obj.AUNDEF)
  440. p = appendp(p, AEnd)
  441. } else {
  442. // unwinding WebAssembly stack to switch goroutine, return 1
  443. p = appendp(p, ABrIf)
  444. unwindExitBranches = append(unwindExitBranches, p)
  445. }
  446. // jump to before the call if jmpdefer has reset the return address to the call's PC
  447. if call.To.Sym == deferreturn {
  448. // get PC_B from -8(SP)
  449. p = appendp(p, AGet, regAddr(REG_SP))
  450. p = appendp(p, AI32Const, constAddr(8))
  451. p = appendp(p, AI32Sub)
  452. p = appendp(p, AI32Load16U, constAddr(0))
  453. p = appendp(p, ATee, regAddr(REG_PC_B))
  454. p = appendp(p, AI32Const, constAddr(call.Pc))
  455. p = appendp(p, AI32Eq)
  456. p = appendp(p, ABrIf, constAddr(0))
  457. p = appendp(p, AEnd) // end of Loop
  458. }
  459. case obj.ARET, ARETUNWIND:
  460. ret := *p
  461. p.As = obj.ANOP
  462. if framesize > 0 {
  463. // SP += framesize
  464. p = appendp(p, AGet, regAddr(REG_SP))
  465. p = appendp(p, AI32Const, constAddr(framesize))
  466. p = appendp(p, AI32Add)
  467. p = appendp(p, ASet, regAddr(REG_SP))
  468. // TODO(neelance): This should theoretically set Spadj, but it only works without.
  469. // p.Spadj = int32(-framesize)
  470. }
  471. if ret.To.Type == obj.TYPE_MEM {
  472. // Set PC_B parameter to function entry.
  473. p = appendp(p, AI32Const, constAddr(0))
  474. // low-level WebAssembly call to function
  475. p = appendp(p, ACall, ret.To)
  476. p = appendp(p, AReturn)
  477. break
  478. }
  479. // SP += 8
  480. p = appendp(p, AGet, regAddr(REG_SP))
  481. p = appendp(p, AI32Const, constAddr(8))
  482. p = appendp(p, AI32Add)
  483. p = appendp(p, ASet, regAddr(REG_SP))
  484. if ret.As == ARETUNWIND {
  485. // function needs to unwind the WebAssembly stack, return 1
  486. p = appendp(p, AI32Const, constAddr(1))
  487. p = appendp(p, AReturn)
  488. break
  489. }
  490. // not unwinding the WebAssembly stack, return 0
  491. p = appendp(p, AI32Const, constAddr(0))
  492. p = appendp(p, AReturn)
  493. }
  494. }
  495. for p := s.Func.Text; p != nil; p = p.Link {
  496. switch p.From.Name {
  497. case obj.NAME_AUTO:
  498. p.From.Offset += int64(framesize)
  499. case obj.NAME_PARAM:
  500. p.From.Reg = REG_SP
  501. p.From.Offset += int64(framesize) + 8 // parameters are after the frame and the 8-byte return address
  502. }
  503. switch p.To.Name {
  504. case obj.NAME_AUTO:
  505. p.To.Offset += int64(framesize)
  506. case obj.NAME_PARAM:
  507. p.To.Reg = REG_SP
  508. p.To.Offset += int64(framesize) + 8 // parameters are after the frame and the 8-byte return address
  509. }
  510. switch p.As {
  511. case AGet:
  512. if p.From.Type == obj.TYPE_ADDR {
  513. get := *p
  514. p.As = obj.ANOP
  515. switch get.From.Name {
  516. case obj.NAME_EXTERN:
  517. p = appendp(p, AI64Const, get.From)
  518. case obj.NAME_AUTO, obj.NAME_PARAM:
  519. p = appendp(p, AGet, regAddr(get.From.Reg))
  520. if get.From.Reg == REG_SP {
  521. p = appendp(p, AI64ExtendI32U)
  522. }
  523. if get.From.Offset != 0 {
  524. p = appendp(p, AI64Const, constAddr(get.From.Offset))
  525. p = appendp(p, AI64Add)
  526. }
  527. default:
  528. panic("bad Get: invalid name")
  529. }
  530. }
  531. case AI32Load, AI64Load, AF32Load, AF64Load, AI32Load8S, AI32Load8U, AI32Load16S, AI32Load16U, AI64Load8S, AI64Load8U, AI64Load16S, AI64Load16U, AI64Load32S, AI64Load32U:
  532. if p.From.Type == obj.TYPE_MEM {
  533. as := p.As
  534. from := p.From
  535. p.As = AGet
  536. p.From = regAddr(from.Reg)
  537. if from.Reg != REG_SP {
  538. p = appendp(p, AI32WrapI64)
  539. }
  540. p = appendp(p, as, constAddr(from.Offset))
  541. }
  542. case AMOVB, AMOVH, AMOVW, AMOVD:
  543. mov := *p
  544. p.As = obj.ANOP
  545. var loadAs obj.As
  546. var storeAs obj.As
  547. switch mov.As {
  548. case AMOVB:
  549. loadAs = AI64Load8U
  550. storeAs = AI64Store8
  551. case AMOVH:
  552. loadAs = AI64Load16U
  553. storeAs = AI64Store16
  554. case AMOVW:
  555. loadAs = AI64Load32U
  556. storeAs = AI64Store32
  557. case AMOVD:
  558. loadAs = AI64Load
  559. storeAs = AI64Store
  560. }
  561. appendValue := func() {
  562. switch mov.From.Type {
  563. case obj.TYPE_CONST:
  564. p = appendp(p, AI64Const, constAddr(mov.From.Offset))
  565. case obj.TYPE_ADDR:
  566. switch mov.From.Name {
  567. case obj.NAME_NONE, obj.NAME_PARAM, obj.NAME_AUTO:
  568. p = appendp(p, AGet, regAddr(mov.From.Reg))
  569. if mov.From.Reg == REG_SP {
  570. p = appendp(p, AI64ExtendI32U)
  571. }
  572. p = appendp(p, AI64Const, constAddr(mov.From.Offset))
  573. p = appendp(p, AI64Add)
  574. case obj.NAME_EXTERN:
  575. p = appendp(p, AI64Const, mov.From)
  576. default:
  577. panic("bad name for MOV")
  578. }
  579. case obj.TYPE_REG:
  580. p = appendp(p, AGet, mov.From)
  581. if mov.From.Reg == REG_SP {
  582. p = appendp(p, AI64ExtendI32U)
  583. }
  584. case obj.TYPE_MEM:
  585. p = appendp(p, AGet, regAddr(mov.From.Reg))
  586. if mov.From.Reg != REG_SP {
  587. p = appendp(p, AI32WrapI64)
  588. }
  589. p = appendp(p, loadAs, constAddr(mov.From.Offset))
  590. default:
  591. panic("bad MOV type")
  592. }
  593. }
  594. switch mov.To.Type {
  595. case obj.TYPE_REG:
  596. appendValue()
  597. if mov.To.Reg == REG_SP {
  598. p = appendp(p, AI32WrapI64)
  599. }
  600. p = appendp(p, ASet, mov.To)
  601. case obj.TYPE_MEM:
  602. switch mov.To.Name {
  603. case obj.NAME_NONE, obj.NAME_PARAM:
  604. p = appendp(p, AGet, regAddr(mov.To.Reg))
  605. if mov.To.Reg != REG_SP {
  606. p = appendp(p, AI32WrapI64)
  607. }
  608. case obj.NAME_EXTERN:
  609. p = appendp(p, AI32Const, obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_EXTERN, Sym: mov.To.Sym})
  610. default:
  611. panic("bad MOV name")
  612. }
  613. appendValue()
  614. p = appendp(p, storeAs, constAddr(mov.To.Offset))
  615. default:
  616. panic("bad MOV type")
  617. }
  618. case ACallImport:
  619. p.As = obj.ANOP
  620. p = appendp(p, AGet, regAddr(REG_SP))
  621. p = appendp(p, ACall, obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: s})
  622. p.Mark = WasmImport
  623. }
  624. }
  625. {
  626. p := s.Func.Text
  627. if len(unwindExitBranches) > 0 {
  628. p = appendp(p, ABlock) // unwindExit, used to return 1 when unwinding the stack
  629. for _, b := range unwindExitBranches {
  630. b.To = obj.Addr{Type: obj.TYPE_BRANCH, Val: p}
  631. }
  632. }
  633. if len(entryPointLoopBranches) > 0 {
  634. p = appendp(p, ALoop) // entryPointLoop, used to jump between basic blocks
  635. for _, b := range entryPointLoopBranches {
  636. b.To = obj.Addr{Type: obj.TYPE_BRANCH, Val: p}
  637. }
  638. }
  639. if numResumePoints > 0 {
  640. // Add Block instructions for resume points and BrTable to jump to selected resume point.
  641. for i := 0; i < numResumePoints+1; i++ {
  642. p = appendp(p, ABlock)
  643. }
  644. p = appendp(p, AGet, regAddr(REG_PC_B)) // read next basic block from PC_B
  645. p = appendp(p, ABrTable, obj.Addr{Val: tableIdxs})
  646. p = appendp(p, AEnd) // end of Block
  647. }
  648. for p.Link != nil {
  649. p = p.Link // function instructions
  650. }
  651. if len(entryPointLoopBranches) > 0 {
  652. p = appendp(p, AEnd) // end of entryPointLoop
  653. }
  654. p = appendp(p, obj.AUNDEF)
  655. if len(unwindExitBranches) > 0 {
  656. p = appendp(p, AEnd) // end of unwindExit
  657. p = appendp(p, AI32Const, constAddr(1))
  658. }
  659. }
  660. currentDepth = 0
  661. blockDepths := make(map[*obj.Prog]int)
  662. for p := s.Func.Text; p != nil; p = p.Link {
  663. switch p.As {
  664. case ABlock, ALoop, AIf:
  665. currentDepth++
  666. blockDepths[p] = currentDepth
  667. case AEnd:
  668. currentDepth--
  669. }
  670. switch p.As {
  671. case ABr, ABrIf:
  672. if p.To.Type == obj.TYPE_BRANCH {
  673. blockDepth, ok := blockDepths[p.To.Val.(*obj.Prog)]
  674. if !ok {
  675. panic("label not at block")
  676. }
  677. p.To = constAddr(int64(currentDepth - blockDepth))
  678. }
  679. }
  680. }
  681. }
  682. func constAddr(value int64) obj.Addr {
  683. return obj.Addr{Type: obj.TYPE_CONST, Offset: value}
  684. }
  685. func regAddr(reg int16) obj.Addr {
  686. return obj.Addr{Type: obj.TYPE_REG, Reg: reg}
  687. }
  688. // Most of the Go functions has a single parameter (PC_B) in
  689. // Wasm ABI. This is a list of exceptions.
  690. var notUsePC_B = map[string]bool{
  691. "_rt0_wasm_js": true,
  692. "wasm_export_run": true,
  693. "wasm_export_resume": true,
  694. "wasm_export_getsp": true,
  695. "wasm_pc_f_loop": true,
  696. "runtime.wasmMove": true,
  697. "runtime.wasmZero": true,
  698. "runtime.wasmDiv": true,
  699. "runtime.wasmTruncS": true,
  700. "runtime.wasmTruncU": true,
  701. "runtime.gcWriteBarrier": true,
  702. "cmpbody": true,
  703. "memeqbody": true,
  704. "memcmp": true,
  705. "memchr": true,
  706. }
  707. func assemble(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
  708. type regVar struct {
  709. global bool
  710. index uint64
  711. }
  712. type varDecl struct {
  713. count uint64
  714. typ valueType
  715. }
  716. hasLocalSP := false
  717. regVars := [MAXREG - MINREG]*regVar{
  718. REG_SP - MINREG: {true, 0},
  719. REG_CTXT - MINREG: {true, 1},
  720. REG_g - MINREG: {true, 2},
  721. REG_RET0 - MINREG: {true, 3},
  722. REG_RET1 - MINREG: {true, 4},
  723. REG_RET2 - MINREG: {true, 5},
  724. REG_RET3 - MINREG: {true, 6},
  725. REG_PAUSE - MINREG: {true, 7},
  726. }
  727. var varDecls []*varDecl
  728. useAssemblyRegMap := func() {
  729. for i := int16(0); i < 16; i++ {
  730. regVars[REG_R0+i-MINREG] = &regVar{false, uint64(i)}
  731. }
  732. }
  733. // Function starts with declaration of locals: numbers and types.
  734. // Some functions use a special calling convention.
  735. switch s.Name {
  736. case "_rt0_wasm_js", "wasm_export_run", "wasm_export_resume", "wasm_export_getsp", "wasm_pc_f_loop",
  737. "runtime.wasmMove", "runtime.wasmZero", "runtime.wasmDiv", "runtime.wasmTruncS", "runtime.wasmTruncU", "memeqbody":
  738. varDecls = []*varDecl{}
  739. useAssemblyRegMap()
  740. case "memchr", "memcmp":
  741. varDecls = []*varDecl{{count: 2, typ: i32}}
  742. useAssemblyRegMap()
  743. case "cmpbody":
  744. varDecls = []*varDecl{{count: 2, typ: i64}}
  745. useAssemblyRegMap()
  746. case "runtime.gcWriteBarrier":
  747. varDecls = []*varDecl{{count: 4, typ: i64}}
  748. useAssemblyRegMap()
  749. default:
  750. // Normal calling convention: PC_B as WebAssembly parameter. First local variable is local SP cache.
  751. regVars[REG_PC_B-MINREG] = &regVar{false, 0}
  752. hasLocalSP = true
  753. var regUsed [MAXREG - MINREG]bool
  754. for p := s.Func.Text; p != nil; p = p.Link {
  755. if p.From.Reg != 0 {
  756. regUsed[p.From.Reg-MINREG] = true
  757. }
  758. if p.To.Reg != 0 {
  759. regUsed[p.To.Reg-MINREG] = true
  760. }
  761. }
  762. regs := []int16{REG_SP}
  763. for reg := int16(REG_R0); reg <= REG_F31; reg++ {
  764. if regUsed[reg-MINREG] {
  765. regs = append(regs, reg)
  766. }
  767. }
  768. var lastDecl *varDecl
  769. for i, reg := range regs {
  770. t := regType(reg)
  771. if lastDecl == nil || lastDecl.typ != t {
  772. lastDecl = &varDecl{
  773. count: 0,
  774. typ: t,
  775. }
  776. varDecls = append(varDecls, lastDecl)
  777. }
  778. lastDecl.count++
  779. if reg != REG_SP {
  780. regVars[reg-MINREG] = &regVar{false, 1 + uint64(i)}
  781. }
  782. }
  783. }
  784. w := new(bytes.Buffer)
  785. writeUleb128(w, uint64(len(varDecls)))
  786. for _, decl := range varDecls {
  787. writeUleb128(w, decl.count)
  788. w.WriteByte(byte(decl.typ))
  789. }
  790. if hasLocalSP {
  791. // Copy SP from its global variable into a local variable. Accessing a local variable is more efficient.
  792. updateLocalSP(w)
  793. }
  794. for p := s.Func.Text; p != nil; p = p.Link {
  795. switch p.As {
  796. case AGet:
  797. if p.From.Type != obj.TYPE_REG {
  798. panic("bad Get: argument is not a register")
  799. }
  800. reg := p.From.Reg
  801. v := regVars[reg-MINREG]
  802. if v == nil {
  803. panic("bad Get: invalid register")
  804. }
  805. if reg == REG_SP && hasLocalSP {
  806. writeOpcode(w, ALocalGet)
  807. writeUleb128(w, 1) // local SP
  808. continue
  809. }
  810. if v.global {
  811. writeOpcode(w, AGlobalGet)
  812. } else {
  813. writeOpcode(w, ALocalGet)
  814. }
  815. writeUleb128(w, v.index)
  816. continue
  817. case ASet:
  818. if p.To.Type != obj.TYPE_REG {
  819. panic("bad Set: argument is not a register")
  820. }
  821. reg := p.To.Reg
  822. v := regVars[reg-MINREG]
  823. if v == nil {
  824. panic("bad Set: invalid register")
  825. }
  826. if reg == REG_SP && hasLocalSP {
  827. writeOpcode(w, ALocalTee)
  828. writeUleb128(w, 1) // local SP
  829. }
  830. if v.global {
  831. writeOpcode(w, AGlobalSet)
  832. } else {
  833. if p.Link.As == AGet && p.Link.From.Reg == reg {
  834. writeOpcode(w, ALocalTee)
  835. p = p.Link
  836. } else {
  837. writeOpcode(w, ALocalSet)
  838. }
  839. }
  840. writeUleb128(w, v.index)
  841. continue
  842. case ATee:
  843. if p.To.Type != obj.TYPE_REG {
  844. panic("bad Tee: argument is not a register")
  845. }
  846. reg := p.To.Reg
  847. v := regVars[reg-MINREG]
  848. if v == nil {
  849. panic("bad Tee: invalid register")
  850. }
  851. writeOpcode(w, ALocalTee)
  852. writeUleb128(w, v.index)
  853. continue
  854. case ANot:
  855. writeOpcode(w, AI32Eqz)
  856. continue
  857. case obj.AUNDEF:
  858. writeOpcode(w, AUnreachable)
  859. continue
  860. case obj.ANOP, obj.ATEXT, obj.AFUNCDATA, obj.APCDATA:
  861. // ignore
  862. continue
  863. }
  864. writeOpcode(w, p.As)
  865. switch p.As {
  866. case ABlock, ALoop, AIf:
  867. if p.From.Offset != 0 {
  868. // block type, rarely used, e.g. for code compiled with emscripten
  869. w.WriteByte(0x80 - byte(p.From.Offset))
  870. continue
  871. }
  872. w.WriteByte(0x40)
  873. case ABr, ABrIf:
  874. if p.To.Type != obj.TYPE_CONST {
  875. panic("bad Br/BrIf")
  876. }
  877. writeUleb128(w, uint64(p.To.Offset))
  878. case ABrTable:
  879. idxs := p.To.Val.([]uint64)
  880. writeUleb128(w, uint64(len(idxs)-1))
  881. for _, idx := range idxs {
  882. writeUleb128(w, idx)
  883. }
  884. case ACall:
  885. switch p.To.Type {
  886. case obj.TYPE_CONST:
  887. writeUleb128(w, uint64(p.To.Offset))
  888. case obj.TYPE_MEM:
  889. if p.To.Name != obj.NAME_EXTERN && p.To.Name != obj.NAME_STATIC {
  890. fmt.Println(p.To)
  891. panic("bad name for Call")
  892. }
  893. r := obj.Addrel(s)
  894. r.Off = int32(w.Len())
  895. r.Type = objabi.R_CALL
  896. if p.Mark&WasmImport != 0 {
  897. r.Type = objabi.R_WASMIMPORT
  898. }
  899. r.Sym = p.To.Sym
  900. if hasLocalSP {
  901. // The stack may have moved, which changes SP. Update the local SP variable.
  902. updateLocalSP(w)
  903. }
  904. default:
  905. panic("bad type for Call")
  906. }
  907. case ACallIndirect:
  908. writeUleb128(w, uint64(p.To.Offset))
  909. w.WriteByte(0x00) // reserved value
  910. if hasLocalSP {
  911. // The stack may have moved, which changes SP. Update the local SP variable.
  912. updateLocalSP(w)
  913. }
  914. case AI32Const, AI64Const:
  915. if p.From.Name == obj.NAME_EXTERN {
  916. r := obj.Addrel(s)
  917. r.Off = int32(w.Len())
  918. r.Type = objabi.R_ADDR
  919. r.Sym = p.From.Sym
  920. r.Add = p.From.Offset
  921. break
  922. }
  923. writeSleb128(w, p.From.Offset)
  924. case AF32Const:
  925. b := make([]byte, 4)
  926. binary.LittleEndian.PutUint32(b, math.Float32bits(float32(p.From.Val.(float64))))
  927. w.Write(b)
  928. case AF64Const:
  929. b := make([]byte, 8)
  930. binary.LittleEndian.PutUint64(b, math.Float64bits(p.From.Val.(float64)))
  931. w.Write(b)
  932. case AI32Load, AI64Load, AF32Load, AF64Load, AI32Load8S, AI32Load8U, AI32Load16S, AI32Load16U, AI64Load8S, AI64Load8U, AI64Load16S, AI64Load16U, AI64Load32S, AI64Load32U:
  933. if p.From.Offset < 0 {
  934. panic("negative offset for *Load")
  935. }
  936. if p.From.Type != obj.TYPE_CONST {
  937. panic("bad type for *Load")
  938. }
  939. if p.From.Offset > math.MaxUint32 {
  940. ctxt.Diag("bad offset in %v", p)
  941. }
  942. writeUleb128(w, align(p.As))
  943. writeUleb128(w, uint64(p.From.Offset))
  944. case AI32Store, AI64Store, AF32Store, AF64Store, AI32Store8, AI32Store16, AI64Store8, AI64Store16, AI64Store32:
  945. if p.To.Offset < 0 {
  946. panic("negative offset")
  947. }
  948. if p.From.Offset > math.MaxUint32 {
  949. ctxt.Diag("bad offset in %v", p)
  950. }
  951. writeUleb128(w, align(p.As))
  952. writeUleb128(w, uint64(p.To.Offset))
  953. case ACurrentMemory, AGrowMemory:
  954. w.WriteByte(0x00)
  955. }
  956. }
  957. w.WriteByte(0x0b) // end
  958. s.P = w.Bytes()
  959. }
  960. func updateLocalSP(w *bytes.Buffer) {
  961. writeOpcode(w, AGlobalGet)
  962. writeUleb128(w, 0) // global SP
  963. writeOpcode(w, ALocalSet)
  964. writeUleb128(w, 1) // local SP
  965. }
  966. func writeOpcode(w *bytes.Buffer, as obj.As) {
  967. switch {
  968. case as < AUnreachable:
  969. panic(fmt.Sprintf("unexpected assembler op: %s", as))
  970. case as < AEnd:
  971. w.WriteByte(byte(as - AUnreachable + 0x00))
  972. case as < ADrop:
  973. w.WriteByte(byte(as - AEnd + 0x0B))
  974. case as < ALocalGet:
  975. w.WriteByte(byte(as - ADrop + 0x1A))
  976. case as < AI32Load:
  977. w.WriteByte(byte(as - ALocalGet + 0x20))
  978. case as < AI32TruncSatF32S:
  979. w.WriteByte(byte(as - AI32Load + 0x28))
  980. case as < ALast:
  981. w.WriteByte(0xFC)
  982. w.WriteByte(byte(as - AI32TruncSatF32S + 0x00))
  983. default:
  984. panic(fmt.Sprintf("unexpected assembler op: %s", as))
  985. }
  986. }
  987. type valueType byte
  988. const (
  989. i32 valueType = 0x7F
  990. i64 valueType = 0x7E
  991. f32 valueType = 0x7D
  992. f64 valueType = 0x7C
  993. )
  994. func regType(reg int16) valueType {
  995. switch {
  996. case reg == REG_SP:
  997. return i32
  998. case reg >= REG_R0 && reg <= REG_R15:
  999. return i64
  1000. case reg >= REG_F0 && reg <= REG_F15:
  1001. return f32
  1002. case reg >= REG_F16 && reg <= REG_F31:
  1003. return f64
  1004. default:
  1005. panic("invalid register")
  1006. }
  1007. }
  1008. func align(as obj.As) uint64 {
  1009. switch as {
  1010. case AI32Load8S, AI32Load8U, AI64Load8S, AI64Load8U, AI32Store8, AI64Store8:
  1011. return 0
  1012. case AI32Load16S, AI32Load16U, AI64Load16S, AI64Load16U, AI32Store16, AI64Store16:
  1013. return 1
  1014. case AI32Load, AF32Load, AI64Load32S, AI64Load32U, AI32Store, AF32Store, AI64Store32:
  1015. return 2
  1016. case AI64Load, AF64Load, AI64Store, AF64Store:
  1017. return 3
  1018. default:
  1019. panic("align: bad op")
  1020. }
  1021. }
  1022. func writeUleb128(w io.ByteWriter, v uint64) {
  1023. if v < 128 {
  1024. w.WriteByte(uint8(v))
  1025. return
  1026. }
  1027. more := true
  1028. for more {
  1029. c := uint8(v & 0x7f)
  1030. v >>= 7
  1031. more = v != 0
  1032. if more {
  1033. c |= 0x80
  1034. }
  1035. w.WriteByte(c)
  1036. }
  1037. }
  1038. func writeSleb128(w io.ByteWriter, v int64) {
  1039. more := true
  1040. for more {
  1041. c := uint8(v & 0x7f)
  1042. s := uint8(v & 0x40)
  1043. v >>= 7
  1044. more = !((v == 0 && s == 0) || (v == -1 && s != 0))
  1045. if more {
  1046. c |= 0x80
  1047. }
  1048. w.WriteByte(c)
  1049. }
  1050. }