You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

727 lines
18 KiB

  1. // Copyright 2016 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 bpf
  5. import "fmt"
  6. // An Instruction is one instruction executed by the BPF virtual
  7. // machine.
  8. type Instruction interface {
  9. // Assemble assembles the Instruction into a RawInstruction.
  10. Assemble() (RawInstruction, error)
  11. }
  12. // A RawInstruction is a raw BPF virtual machine instruction.
  13. type RawInstruction struct {
  14. // Operation to execute.
  15. Op uint16
  16. // For conditional jump instructions, the number of instructions
  17. // to skip if the condition is true/false.
  18. Jt uint8
  19. Jf uint8
  20. // Constant parameter. The meaning depends on the Op.
  21. K uint32
  22. }
  23. // Assemble implements the Instruction Assemble method.
  24. func (ri RawInstruction) Assemble() (RawInstruction, error) { return ri, nil }
  25. // Disassemble parses ri into an Instruction and returns it. If ri is
  26. // not recognized by this package, ri itself is returned.
  27. func (ri RawInstruction) Disassemble() Instruction {
  28. switch ri.Op & opMaskCls {
  29. case opClsLoadA, opClsLoadX:
  30. reg := Register(ri.Op & opMaskLoadDest)
  31. sz := 0
  32. switch ri.Op & opMaskLoadWidth {
  33. case opLoadWidth4:
  34. sz = 4
  35. case opLoadWidth2:
  36. sz = 2
  37. case opLoadWidth1:
  38. sz = 1
  39. default:
  40. return ri
  41. }
  42. switch ri.Op & opMaskLoadMode {
  43. case opAddrModeImmediate:
  44. if sz != 4 {
  45. return ri
  46. }
  47. return LoadConstant{Dst: reg, Val: ri.K}
  48. case opAddrModeScratch:
  49. if sz != 4 || ri.K > 15 {
  50. return ri
  51. }
  52. return LoadScratch{Dst: reg, N: int(ri.K)}
  53. case opAddrModeAbsolute:
  54. if ri.K > extOffset+0xffffffff {
  55. return LoadExtension{Num: Extension(-extOffset + ri.K)}
  56. }
  57. return LoadAbsolute{Size: sz, Off: ri.K}
  58. case opAddrModeIndirect:
  59. return LoadIndirect{Size: sz, Off: ri.K}
  60. case opAddrModePacketLen:
  61. if sz != 4 {
  62. return ri
  63. }
  64. return LoadExtension{Num: ExtLen}
  65. case opAddrModeMemShift:
  66. return LoadMemShift{Off: ri.K}
  67. default:
  68. return ri
  69. }
  70. case opClsStoreA:
  71. if ri.Op != opClsStoreA || ri.K > 15 {
  72. return ri
  73. }
  74. return StoreScratch{Src: RegA, N: int(ri.K)}
  75. case opClsStoreX:
  76. if ri.Op != opClsStoreX || ri.K > 15 {
  77. return ri
  78. }
  79. return StoreScratch{Src: RegX, N: int(ri.K)}
  80. case opClsALU:
  81. switch op := ALUOp(ri.Op & opMaskOperator); op {
  82. case ALUOpAdd, ALUOpSub, ALUOpMul, ALUOpDiv, ALUOpOr, ALUOpAnd, ALUOpShiftLeft, ALUOpShiftRight, ALUOpMod, ALUOpXor:
  83. switch operand := opOperand(ri.Op & opMaskOperand); operand {
  84. case opOperandX:
  85. return ALUOpX{Op: op}
  86. case opOperandConstant:
  87. return ALUOpConstant{Op: op, Val: ri.K}
  88. default:
  89. return ri
  90. }
  91. case aluOpNeg:
  92. return NegateA{}
  93. default:
  94. return ri
  95. }
  96. case opClsJump:
  97. switch op := jumpOp(ri.Op & opMaskOperator); op {
  98. case opJumpAlways:
  99. return Jump{Skip: ri.K}
  100. case opJumpEqual, opJumpGT, opJumpGE, opJumpSet:
  101. cond, skipTrue, skipFalse := jumpOpToTest(op, ri.Jt, ri.Jf)
  102. switch operand := opOperand(ri.Op & opMaskOperand); operand {
  103. case opOperandX:
  104. return JumpIfX{Cond: cond, SkipTrue: skipTrue, SkipFalse: skipFalse}
  105. case opOperandConstant:
  106. return JumpIf{Cond: cond, Val: ri.K, SkipTrue: skipTrue, SkipFalse: skipFalse}
  107. default:
  108. return ri
  109. }
  110. default:
  111. return ri
  112. }
  113. case opClsReturn:
  114. switch ri.Op {
  115. case opClsReturn | opRetSrcA:
  116. return RetA{}
  117. case opClsReturn | opRetSrcConstant:
  118. return RetConstant{Val: ri.K}
  119. default:
  120. return ri
  121. }
  122. case opClsMisc:
  123. switch ri.Op {
  124. case opClsMisc | opMiscTAX:
  125. return TAX{}
  126. case opClsMisc | opMiscTXA:
  127. return TXA{}
  128. default:
  129. return ri
  130. }
  131. default:
  132. panic("unreachable") // switch is exhaustive on the bit pattern
  133. }
  134. }
  135. func jumpOpToTest(op jumpOp, skipTrue uint8, skipFalse uint8) (JumpTest, uint8, uint8) {
  136. var test JumpTest
  137. // Decode "fake" jump conditions that don't appear in machine code
  138. // Ensures the Assemble -> Disassemble stage recreates the same instructions
  139. // See https://github.com/golang/go/issues/18470
  140. if skipTrue == 0 {
  141. switch op {
  142. case opJumpEqual:
  143. test = JumpNotEqual
  144. case opJumpGT:
  145. test = JumpLessOrEqual
  146. case opJumpGE:
  147. test = JumpLessThan
  148. case opJumpSet:
  149. test = JumpBitsNotSet
  150. }
  151. return test, skipFalse, 0
  152. }
  153. switch op {
  154. case opJumpEqual:
  155. test = JumpEqual
  156. case opJumpGT:
  157. test = JumpGreaterThan
  158. case opJumpGE:
  159. test = JumpGreaterOrEqual
  160. case opJumpSet:
  161. test = JumpBitsSet
  162. }
  163. return test, skipTrue, skipFalse
  164. }
  165. // LoadConstant loads Val into register Dst.
  166. type LoadConstant struct {
  167. Dst Register
  168. Val uint32
  169. }
  170. // Assemble implements the Instruction Assemble method.
  171. func (a LoadConstant) Assemble() (RawInstruction, error) {
  172. return assembleLoad(a.Dst, 4, opAddrModeImmediate, a.Val)
  173. }
  174. // String returns the instruction in assembler notation.
  175. func (a LoadConstant) String() string {
  176. switch a.Dst {
  177. case RegA:
  178. return fmt.Sprintf("ld #%d", a.Val)
  179. case RegX:
  180. return fmt.Sprintf("ldx #%d", a.Val)
  181. default:
  182. return fmt.Sprintf("unknown instruction: %#v", a)
  183. }
  184. }
  185. // LoadScratch loads scratch[N] into register Dst.
  186. type LoadScratch struct {
  187. Dst Register
  188. N int // 0-15
  189. }
  190. // Assemble implements the Instruction Assemble method.
  191. func (a LoadScratch) Assemble() (RawInstruction, error) {
  192. if a.N < 0 || a.N > 15 {
  193. return RawInstruction{}, fmt.Errorf("invalid scratch slot %d", a.N)
  194. }
  195. return assembleLoad(a.Dst, 4, opAddrModeScratch, uint32(a.N))
  196. }
  197. // String returns the instruction in assembler notation.
  198. func (a LoadScratch) String() string {
  199. switch a.Dst {
  200. case RegA:
  201. return fmt.Sprintf("ld M[%d]", a.N)
  202. case RegX:
  203. return fmt.Sprintf("ldx M[%d]", a.N)
  204. default:
  205. return fmt.Sprintf("unknown instruction: %#v", a)
  206. }
  207. }
  208. // LoadAbsolute loads packet[Off:Off+Size] as an integer value into
  209. // register A.
  210. type LoadAbsolute struct {
  211. Off uint32
  212. Size int // 1, 2 or 4
  213. }
  214. // Assemble implements the Instruction Assemble method.
  215. func (a LoadAbsolute) Assemble() (RawInstruction, error) {
  216. return assembleLoad(RegA, a.Size, opAddrModeAbsolute, a.Off)
  217. }
  218. // String returns the instruction in assembler notation.
  219. func (a LoadAbsolute) String() string {
  220. switch a.Size {
  221. case 1: // byte
  222. return fmt.Sprintf("ldb [%d]", a.Off)
  223. case 2: // half word
  224. return fmt.Sprintf("ldh [%d]", a.Off)
  225. case 4: // word
  226. if a.Off > extOffset+0xffffffff {
  227. return LoadExtension{Num: Extension(a.Off + 0x1000)}.String()
  228. }
  229. return fmt.Sprintf("ld [%d]", a.Off)
  230. default:
  231. return fmt.Sprintf("unknown instruction: %#v", a)
  232. }
  233. }
  234. // LoadIndirect loads packet[X+Off:X+Off+Size] as an integer value
  235. // into register A.
  236. type LoadIndirect struct {
  237. Off uint32
  238. Size int // 1, 2 or 4
  239. }
  240. // Assemble implements the Instruction Assemble method.
  241. func (a LoadIndirect) Assemble() (RawInstruction, error) {
  242. return assembleLoad(RegA, a.Size, opAddrModeIndirect, a.Off)
  243. }
  244. // String returns the instruction in assembler notation.
  245. func (a LoadIndirect) String() string {
  246. switch a.Size {
  247. case 1: // byte
  248. return fmt.Sprintf("ldb [x + %d]", a.Off)
  249. case 2: // half word
  250. return fmt.Sprintf("ldh [x + %d]", a.Off)
  251. case 4: // word
  252. return fmt.Sprintf("ld [x + %d]", a.Off)
  253. default:
  254. return fmt.Sprintf("unknown instruction: %#v", a)
  255. }
  256. }
  257. // LoadMemShift multiplies the first 4 bits of the byte at packet[Off]
  258. // by 4 and stores the result in register X.
  259. //
  260. // This instruction is mainly useful to load into X the length of an
  261. // IPv4 packet header in a single instruction, rather than have to do
  262. // the arithmetic on the header's first byte by hand.
  263. type LoadMemShift struct {
  264. Off uint32
  265. }
  266. // Assemble implements the Instruction Assemble method.
  267. func (a LoadMemShift) Assemble() (RawInstruction, error) {
  268. return assembleLoad(RegX, 1, opAddrModeMemShift, a.Off)
  269. }
  270. // String returns the instruction in assembler notation.
  271. func (a LoadMemShift) String() string {
  272. return fmt.Sprintf("ldx 4*([%d]&0xf)", a.Off)
  273. }
  274. // LoadExtension invokes a linux-specific extension and stores the
  275. // result in register A.
  276. type LoadExtension struct {
  277. Num Extension
  278. }
  279. // Assemble implements the Instruction Assemble method.
  280. func (a LoadExtension) Assemble() (RawInstruction, error) {
  281. if a.Num == ExtLen {
  282. return assembleLoad(RegA, 4, opAddrModePacketLen, 0)
  283. }
  284. return assembleLoad(RegA, 4, opAddrModeAbsolute, uint32(extOffset+a.Num))
  285. }
  286. // String returns the instruction in assembler notation.
  287. func (a LoadExtension) String() string {
  288. switch a.Num {
  289. case ExtLen:
  290. return "ld #len"
  291. case ExtProto:
  292. return "ld #proto"
  293. case ExtType:
  294. return "ld #type"
  295. case ExtPayloadOffset:
  296. return "ld #poff"
  297. case ExtInterfaceIndex:
  298. return "ld #ifidx"
  299. case ExtNetlinkAttr:
  300. return "ld #nla"
  301. case ExtNetlinkAttrNested:
  302. return "ld #nlan"
  303. case ExtMark:
  304. return "ld #mark"
  305. case ExtQueue:
  306. return "ld #queue"
  307. case ExtLinkLayerType:
  308. return "ld #hatype"
  309. case ExtRXHash:
  310. return "ld #rxhash"
  311. case ExtCPUID:
  312. return "ld #cpu"
  313. case ExtVLANTag:
  314. return "ld #vlan_tci"
  315. case ExtVLANTagPresent:
  316. return "ld #vlan_avail"
  317. case ExtVLANProto:
  318. return "ld #vlan_tpid"
  319. case ExtRand:
  320. return "ld #rand"
  321. default:
  322. return fmt.Sprintf("unknown instruction: %#v", a)
  323. }
  324. }
  325. // StoreScratch stores register Src into scratch[N].
  326. type StoreScratch struct {
  327. Src Register
  328. N int // 0-15
  329. }
  330. // Assemble implements the Instruction Assemble method.
  331. func (a StoreScratch) Assemble() (RawInstruction, error) {
  332. if a.N < 0 || a.N > 15 {
  333. return RawInstruction{}, fmt.Errorf("invalid scratch slot %d", a.N)
  334. }
  335. var op uint16
  336. switch a.Src {
  337. case RegA:
  338. op = opClsStoreA
  339. case RegX:
  340. op = opClsStoreX
  341. default:
  342. return RawInstruction{}, fmt.Errorf("invalid source register %v", a.Src)
  343. }
  344. return RawInstruction{
  345. Op: op,
  346. K: uint32(a.N),
  347. }, nil
  348. }
  349. // String returns the instruction in assembler notation.
  350. func (a StoreScratch) String() string {
  351. switch a.Src {
  352. case RegA:
  353. return fmt.Sprintf("st M[%d]", a.N)
  354. case RegX:
  355. return fmt.Sprintf("stx M[%d]", a.N)
  356. default:
  357. return fmt.Sprintf("unknown instruction: %#v", a)
  358. }
  359. }
  360. // ALUOpConstant executes A = A <Op> Val.
  361. type ALUOpConstant struct {
  362. Op ALUOp
  363. Val uint32
  364. }
  365. // Assemble implements the Instruction Assemble method.
  366. func (a ALUOpConstant) Assemble() (RawInstruction, error) {
  367. return RawInstruction{
  368. Op: opClsALU | uint16(opOperandConstant) | uint16(a.Op),
  369. K: a.Val,
  370. }, nil
  371. }
  372. // String returns the instruction in assembler notation.
  373. func (a ALUOpConstant) String() string {
  374. switch a.Op {
  375. case ALUOpAdd:
  376. return fmt.Sprintf("add #%d", a.Val)
  377. case ALUOpSub:
  378. return fmt.Sprintf("sub #%d", a.Val)
  379. case ALUOpMul:
  380. return fmt.Sprintf("mul #%d", a.Val)
  381. case ALUOpDiv:
  382. return fmt.Sprintf("div #%d", a.Val)
  383. case ALUOpMod:
  384. return fmt.Sprintf("mod #%d", a.Val)
  385. case ALUOpAnd:
  386. return fmt.Sprintf("and #%d", a.Val)
  387. case ALUOpOr:
  388. return fmt.Sprintf("or #%d", a.Val)
  389. case ALUOpXor:
  390. return fmt.Sprintf("xor #%d", a.Val)
  391. case ALUOpShiftLeft:
  392. return fmt.Sprintf("lsh #%d", a.Val)
  393. case ALUOpShiftRight:
  394. return fmt.Sprintf("rsh #%d", a.Val)
  395. default:
  396. return fmt.Sprintf("unknown instruction: %#v", a)
  397. }
  398. }
  399. // ALUOpX executes A = A <Op> X
  400. type ALUOpX struct {
  401. Op ALUOp
  402. }
  403. // Assemble implements the Instruction Assemble method.
  404. func (a ALUOpX) Assemble() (RawInstruction, error) {
  405. return RawInstruction{
  406. Op: opClsALU | uint16(opOperandX) | uint16(a.Op),
  407. }, nil
  408. }
  409. // String returns the instruction in assembler notation.
  410. func (a ALUOpX) String() string {
  411. switch a.Op {
  412. case ALUOpAdd:
  413. return "add x"
  414. case ALUOpSub:
  415. return "sub x"
  416. case ALUOpMul:
  417. return "mul x"
  418. case ALUOpDiv:
  419. return "div x"
  420. case ALUOpMod:
  421. return "mod x"
  422. case ALUOpAnd:
  423. return "and x"
  424. case ALUOpOr:
  425. return "or x"
  426. case ALUOpXor:
  427. return "xor x"
  428. case ALUOpShiftLeft:
  429. return "lsh x"
  430. case ALUOpShiftRight:
  431. return "rsh x"
  432. default:
  433. return fmt.Sprintf("unknown instruction: %#v", a)
  434. }
  435. }
  436. // NegateA executes A = -A.
  437. type NegateA struct{}
  438. // Assemble implements the Instruction Assemble method.
  439. func (a NegateA) Assemble() (RawInstruction, error) {
  440. return RawInstruction{
  441. Op: opClsALU | uint16(aluOpNeg),
  442. }, nil
  443. }
  444. // String returns the instruction in assembler notation.
  445. func (a NegateA) String() string {
  446. return fmt.Sprintf("neg")
  447. }
  448. // Jump skips the following Skip instructions in the program.
  449. type Jump struct {
  450. Skip uint32
  451. }
  452. // Assemble implements the Instruction Assemble method.
  453. func (a Jump) Assemble() (RawInstruction, error) {
  454. return RawInstruction{
  455. Op: opClsJump | uint16(opJumpAlways),
  456. K: a.Skip,
  457. }, nil
  458. }
  459. // String returns the instruction in assembler notation.
  460. func (a Jump) String() string {
  461. return fmt.Sprintf("ja %d", a.Skip)
  462. }
  463. // JumpIf skips the following Skip instructions in the program if A
  464. // <Cond> Val is true.
  465. type JumpIf struct {
  466. Cond JumpTest
  467. Val uint32
  468. SkipTrue uint8
  469. SkipFalse uint8
  470. }
  471. // Assemble implements the Instruction Assemble method.
  472. func (a JumpIf) Assemble() (RawInstruction, error) {
  473. return jumpToRaw(a.Cond, opOperandConstant, a.Val, a.SkipTrue, a.SkipFalse)
  474. }
  475. // String returns the instruction in assembler notation.
  476. func (a JumpIf) String() string {
  477. return jumpToString(a.Cond, fmt.Sprintf("#%d", a.Val), a.SkipTrue, a.SkipFalse)
  478. }
  479. // JumpIfX skips the following Skip instructions in the program if A
  480. // <Cond> X is true.
  481. type JumpIfX struct {
  482. Cond JumpTest
  483. SkipTrue uint8
  484. SkipFalse uint8
  485. }
  486. // Assemble implements the Instruction Assemble method.
  487. func (a JumpIfX) Assemble() (RawInstruction, error) {
  488. return jumpToRaw(a.Cond, opOperandX, 0, a.SkipTrue, a.SkipFalse)
  489. }
  490. // String returns the instruction in assembler notation.
  491. func (a JumpIfX) String() string {
  492. return jumpToString(a.Cond, "x", a.SkipTrue, a.SkipFalse)
  493. }
  494. // jumpToRaw assembles a jump instruction into a RawInstruction
  495. func jumpToRaw(test JumpTest, operand opOperand, k uint32, skipTrue, skipFalse uint8) (RawInstruction, error) {
  496. var (
  497. cond jumpOp
  498. flip bool
  499. )
  500. switch test {
  501. case JumpEqual:
  502. cond = opJumpEqual
  503. case JumpNotEqual:
  504. cond, flip = opJumpEqual, true
  505. case JumpGreaterThan:
  506. cond = opJumpGT
  507. case JumpLessThan:
  508. cond, flip = opJumpGE, true
  509. case JumpGreaterOrEqual:
  510. cond = opJumpGE
  511. case JumpLessOrEqual:
  512. cond, flip = opJumpGT, true
  513. case JumpBitsSet:
  514. cond = opJumpSet
  515. case JumpBitsNotSet:
  516. cond, flip = opJumpSet, true
  517. default:
  518. return RawInstruction{}, fmt.Errorf("unknown JumpTest %v", test)
  519. }
  520. jt, jf := skipTrue, skipFalse
  521. if flip {
  522. jt, jf = jf, jt
  523. }
  524. return RawInstruction{
  525. Op: opClsJump | uint16(cond) | uint16(operand),
  526. Jt: jt,
  527. Jf: jf,
  528. K: k,
  529. }, nil
  530. }
  531. // jumpToString converts a jump instruction to assembler notation
  532. func jumpToString(cond JumpTest, operand string, skipTrue, skipFalse uint8) string {
  533. switch cond {
  534. // K == A
  535. case JumpEqual:
  536. return conditionalJump(operand, skipTrue, skipFalse, "jeq", "jneq")
  537. // K != A
  538. case JumpNotEqual:
  539. return fmt.Sprintf("jneq %s,%d", operand, skipTrue)
  540. // K > A
  541. case JumpGreaterThan:
  542. return conditionalJump(operand, skipTrue, skipFalse, "jgt", "jle")
  543. // K < A
  544. case JumpLessThan:
  545. return fmt.Sprintf("jlt %s,%d", operand, skipTrue)
  546. // K >= A
  547. case JumpGreaterOrEqual:
  548. return conditionalJump(operand, skipTrue, skipFalse, "jge", "jlt")
  549. // K <= A
  550. case JumpLessOrEqual:
  551. return fmt.Sprintf("jle %s,%d", operand, skipTrue)
  552. // K & A != 0
  553. case JumpBitsSet:
  554. if skipFalse > 0 {
  555. return fmt.Sprintf("jset %s,%d,%d", operand, skipTrue, skipFalse)
  556. }
  557. return fmt.Sprintf("jset %s,%d", operand, skipTrue)
  558. // K & A == 0, there is no assembler instruction for JumpBitNotSet, use JumpBitSet and invert skips
  559. case JumpBitsNotSet:
  560. return jumpToString(JumpBitsSet, operand, skipFalse, skipTrue)
  561. default:
  562. return fmt.Sprintf("unknown JumpTest %#v", cond)
  563. }
  564. }
  565. func conditionalJump(operand string, skipTrue, skipFalse uint8, positiveJump, negativeJump string) string {
  566. if skipTrue > 0 {
  567. if skipFalse > 0 {
  568. return fmt.Sprintf("%s %s,%d,%d", positiveJump, operand, skipTrue, skipFalse)
  569. }
  570. return fmt.Sprintf("%s %s,%d", positiveJump, operand, skipTrue)
  571. }
  572. return fmt.Sprintf("%s %s,%d", negativeJump, operand, skipFalse)
  573. }
  574. // RetA exits the BPF program, returning the value of register A.
  575. type RetA struct{}
  576. // Assemble implements the Instruction Assemble method.
  577. func (a RetA) Assemble() (RawInstruction, error) {
  578. return RawInstruction{
  579. Op: opClsReturn | opRetSrcA,
  580. }, nil
  581. }
  582. // String returns the instruction in assembler notation.
  583. func (a RetA) String() string {
  584. return fmt.Sprintf("ret a")
  585. }
  586. // RetConstant exits the BPF program, returning a constant value.
  587. type RetConstant struct {
  588. Val uint32
  589. }
  590. // Assemble implements the Instruction Assemble method.
  591. func (a RetConstant) Assemble() (RawInstruction, error) {
  592. return RawInstruction{
  593. Op: opClsReturn | opRetSrcConstant,
  594. K: a.Val,
  595. }, nil
  596. }
  597. // String returns the instruction in assembler notation.
  598. func (a RetConstant) String() string {
  599. return fmt.Sprintf("ret #%d", a.Val)
  600. }
  601. // TXA copies the value of register X to register A.
  602. type TXA struct{}
  603. // Assemble implements the Instruction Assemble method.
  604. func (a TXA) Assemble() (RawInstruction, error) {
  605. return RawInstruction{
  606. Op: opClsMisc | opMiscTXA,
  607. }, nil
  608. }
  609. // String returns the instruction in assembler notation.
  610. func (a TXA) String() string {
  611. return fmt.Sprintf("txa")
  612. }
  613. // TAX copies the value of register A to register X.
  614. type TAX struct{}
  615. // Assemble implements the Instruction Assemble method.
  616. func (a TAX) Assemble() (RawInstruction, error) {
  617. return RawInstruction{
  618. Op: opClsMisc | opMiscTAX,
  619. }, nil
  620. }
  621. // String returns the instruction in assembler notation.
  622. func (a TAX) String() string {
  623. return fmt.Sprintf("tax")
  624. }
  625. func assembleLoad(dst Register, loadSize int, mode uint16, k uint32) (RawInstruction, error) {
  626. var (
  627. cls uint16
  628. sz uint16
  629. )
  630. switch dst {
  631. case RegA:
  632. cls = opClsLoadA
  633. case RegX:
  634. cls = opClsLoadX
  635. default:
  636. return RawInstruction{}, fmt.Errorf("invalid target register %v", dst)
  637. }
  638. switch loadSize {
  639. case 1:
  640. sz = opLoadWidth1
  641. case 2:
  642. sz = opLoadWidth2
  643. case 4:
  644. sz = opLoadWidth4
  645. default:
  646. return RawInstruction{}, fmt.Errorf("invalid load byte length %d", sz)
  647. }
  648. return RawInstruction{
  649. Op: cls | sz | mode,
  650. K: k,
  651. }, nil
  652. }