aarch64 2.0.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/aarch64/parser.y CHANGED
@@ -14,8 +14,8 @@ rule
14
14
  | adcs
15
15
  | ADD add_body { val[1].apply(@asm, val[0]) }
16
16
  | ADDS add_body { val[1].apply(@asm, val[0]) }
17
- | ADR Xd COMMA imm { @asm.adr(val[1], val[3]) }
18
- | ADRP Xd COMMA imm { @asm.adrp(val[1], val[3]) }
17
+ | ADR Xd COMMA imm_or_label { @asm.adr(val[1], val[3]) }
18
+ | ADRP Xd COMMA imm_or_label { @asm.adrp(val[1], val[3]) }
19
19
  | AND and_body { val[1].apply(@asm, val[0]) }
20
20
  | ANDS and_body { val[1].apply(@asm, val[0]) }
21
21
  | asr { val[0].apply(@asm, :asr) }
@@ -30,8 +30,8 @@ rule
30
30
  | blr
31
31
  | br
32
32
  | BRK imm { @asm.brk(val[1]) }
33
- | CBNZ reg_imm { val[1].apply(@asm, val[0]) }
34
- | CBZ reg_imm { val[1].apply(@asm, val[0]) }
33
+ | CBNZ reg_imm_or_label { val[1].apply(@asm, val[0]) }
34
+ | CBZ reg_imm_or_label { val[1].apply(@asm, val[0]) }
35
35
  | cinc
36
36
  | cinv
37
37
  | clrex
@@ -133,8 +133,8 @@ rule
133
133
  | SXTW xd_wd { val[1].apply(@asm, :sxtw) }
134
134
  | sys
135
135
  | sysl
136
- | TBZ reg_imm_imm { val[1].apply(@asm, val[0]) }
137
- | TBNZ reg_imm_imm { val[1].apply(@asm, val[0]) }
136
+ | TBZ reg_imm_imm_or_label { val[1].apply(@asm, val[0]) }
137
+ | TBNZ reg_imm_imm_or_label { val[1].apply(@asm, val[0]) }
138
138
  | tlbi
139
139
  | tst
140
140
  | UBFIZ ubfiz_body { val[1].apply(@asm, val[0]) }
@@ -150,6 +150,7 @@ rule
150
150
  | WFE { @asm.wfe }
151
151
  | WFI { @asm.wfi }
152
152
  | YIELD { @asm.yield }
153
+ | LABEL_CREATE { register_label(val[0]) }
153
154
  ;
154
155
 
155
156
  adc
@@ -280,8 +281,8 @@ rule
280
281
  at: AT at_op COMMA Xd { @asm.at(val[1].to_sym, val[3]) };
281
282
 
282
283
  b
283
- : B imm { @asm.b(val[1]) }
284
- | B DOT cond imm { @asm.b(val[3], cond: val[2]) }
284
+ : B imm_or_label { @asm.b(val[1]) }
285
+ | B DOT cond imm_or_label { @asm.b(val[3], cond: val[2]) }
285
286
  ;
286
287
 
287
288
  bfi
@@ -313,7 +314,7 @@ rule
313
314
  | AUTDA Xd COMMA SP { @asm.autda(val[1], val[3]) }
314
315
  ;
315
316
 
316
- bl : BL imm { @asm.bl(val[1]) } ;
317
+ bl : BL imm_or_label { @asm.bl(val[1]) } ;
317
318
  blr : BLR Xd { @asm.blr(val[1]) } ;
318
319
  br : BR Xd { @asm.br(val[1]) } ;
319
320
 
@@ -1048,11 +1049,11 @@ rule
1048
1049
  }
1049
1050
  ;
1050
1051
 
1051
- reg_imm_imm
1052
- : Xd COMMA imm COMMA imm {
1052
+ reg_imm_imm_or_label
1053
+ : Xd COMMA imm COMMA imm_or_label {
1053
1054
  result = ThreeArg.new(val[0], val[2], val[4])
1054
1055
  }
1055
- | Wd COMMA imm COMMA imm {
1056
+ | Wd COMMA imm COMMA imm_or_label {
1056
1057
  result = ThreeArg.new(val[0], val[2], val[4])
1057
1058
  }
1058
1059
  ;
@@ -1194,6 +1195,11 @@ rule
1194
1195
  | Xd COMMA imm { result = TwoArg.new(val[0], val[2]) }
1195
1196
  ;
1196
1197
 
1198
+ reg_imm_or_label
1199
+ : Wd COMMA imm_or_label { result = TwoArg.new(val[0], val[2]) }
1200
+ | Xd COMMA imm_or_label { result = TwoArg.new(val[0], val[2]) }
1201
+ ;
1202
+
1197
1203
  reg_reg_imm
1198
1204
  : Wd COMMA Wd COMMA imm {
1199
1205
  result = ThreeArg.new(val[0], val[2], val[4])
@@ -1232,6 +1238,11 @@ rule
1232
1238
  | NUMBER { result = val[0] }
1233
1239
  ;
1234
1240
 
1241
+ imm_or_label
1242
+ : LABEL { result = label_for(val[0]) }
1243
+ | imm
1244
+ ;
1245
+
1235
1246
  xt: Xd | XZR;
1236
1247
 
1237
1248
  cond : EQ | LO | LT | HS | GT | LE | NE | MI | GE | PL | LS | HI | VC | VS;
@@ -1,3 +1,3 @@
1
1
  module AArch64
2
- VERSION = "2.0.1"
2
+ VERSION = "2.1.0"
3
3
  end
data/lib/aarch64.rb CHANGED
@@ -10,6 +10,7 @@ module AArch64
10
10
  alias sp? sp
11
11
  alias zr? zr
12
12
  def integer?; false; end
13
+ def register?; true; end
13
14
  end
14
15
 
15
16
  class XRegister < Register
@@ -22,6 +23,7 @@ module AArch64
22
23
  def opc; 0b10; end
23
24
  def opc2; 0b11; end
24
25
  def opc3; 0b10; end
26
+ def name; "X#{to_i}"; end
25
27
  end
26
28
 
27
29
  class WRegister < Register
@@ -34,6 +36,7 @@ module AArch64
34
36
  def opc; 0b11; end
35
37
  def opc2; 0b10; end
36
38
  def opc3; 0b00; end
39
+ def name; "W#{to_i}"; end
37
40
  end
38
41
 
39
42
  31.times { |i|
@@ -285,6 +288,7 @@ module AArch64
285
288
  end
286
289
 
287
290
  def adrp xd, label
291
+ label = Immediate.new(label) if label.integer?
288
292
  a ADRP.new(xd, label)
289
293
  end
290
294
 
@@ -2715,14 +2719,26 @@ module AArch64
2715
2719
  if label.integer?
2716
2720
  label = wrap_offset_with_label label
2717
2721
  end
2718
- a TBNZ.new(rt, imm, label, rt.sf)
2722
+
2723
+ sf = 0
2724
+ if imm > 31
2725
+ sf = 1
2726
+ imm -= 32
2727
+ end
2728
+ a TBNZ.new(rt, imm, label, sf)
2719
2729
  end
2720
2730
 
2721
2731
  def tbz rt, imm, label
2722
2732
  if label.integer?
2723
2733
  label = wrap_offset_with_label label
2724
2734
  end
2725
- a TBZ.new(rt, imm, label, rt.sf)
2735
+
2736
+ sf = 0
2737
+ if imm > 31
2738
+ sf = 1
2739
+ imm -= 32
2740
+ end
2741
+ a TBZ.new(rt, imm, label, sf)
2726
2742
  end
2727
2743
 
2728
2744
  def tlbi tlbi_op, xt = XZR
@@ -2827,6 +2843,12 @@ module AArch64
2827
2843
  a YIELD.new
2828
2844
  end
2829
2845
 
2846
+ ##
2847
+ # Yields the offset in the instructions
2848
+ def patch_location
2849
+ yield @insns.length * 4
2850
+ end
2851
+
2830
2852
  def write_to io
2831
2853
  io.write to_binary
2832
2854
  end
@@ -287,7 +287,7 @@ class BaseInstructionsTest < AArch64::Test
287
287
  end
288
288
 
289
289
  # SYS #<op1>, C7, <Cm>, #<op2>, <Xt>
290
- assert_one_insn "sys #0, c7, c9, #0, x1" do |asm|
290
+ assert_bytes [0x1, 0x79, 0x8, 0xd5] do |asm|
291
291
  asm.at :s1e1rp, X1
292
292
  end
293
293
  end
@@ -326,17 +326,17 @@ class BaseInstructionsTest < AArch64::Test
326
326
  asm.autiza X1
327
327
  end
328
328
  # AUTIA1716
329
- assert_one_insn "hint #0xc" do |asm|
329
+ assert_one_insn "autia1716" do |asm|
330
330
  asm.autia1716
331
331
  end
332
332
 
333
333
  # AUTIASP
334
- assert_one_insn "hint #0x1d" do |asm|
334
+ assert_one_insn "autiasp" do |asm|
335
335
  asm.autiasp
336
336
  end
337
337
 
338
338
  # AUTIAZ
339
- assert_one_insn "hint #0x1c" do |asm|
339
+ assert_one_insn "autiaz" do |asm|
340
340
  asm.autiaz
341
341
  end
342
342
  end
@@ -360,17 +360,17 @@ class BaseInstructionsTest < AArch64::Test
360
360
  end
361
361
 
362
362
  # AUTIB1716
363
- assert_one_insn "hint #0xe" do |asm|
363
+ assert_one_insn "autib1716" do |asm|
364
364
  asm.autib1716
365
365
  end
366
366
 
367
367
  # AUTIBSP
368
- assert_one_insn "hint #0x1f" do |asm|
368
+ assert_one_insn "autibsp" do |asm|
369
369
  asm.autibsp
370
370
  end
371
371
 
372
372
  # AUTIBZ
373
- assert_one_insn "hint #0x1e" do |asm|
373
+ assert_one_insn "autibz" do |asm|
374
374
  asm.autibz
375
375
  end
376
376
  end
@@ -711,13 +711,14 @@ class BaseInstructionsTest < AArch64::Test
711
711
 
712
712
  def test_BRK
713
713
  # BRK #<imm>
714
- asm.brk 1
715
- assert_one_insn "brk #0x1"
714
+ assert_bytes [0x20, 00, 0x20, 0xd4] do |asm|
715
+ asm.brk 0x1
716
+ end
716
717
  end
717
718
 
718
719
  def test_BTI
719
720
  # BTI {<targets>}
720
- assert_one_insn "hint #0x20" do |asm|
721
+ assert_one_insn "bti" do |asm|
721
722
  asm.bti :c
722
723
  end
723
724
  end
@@ -2774,6 +2775,12 @@ class BaseInstructionsTest < AArch64::Test
2774
2775
  end
2775
2776
  end
2776
2777
 
2778
+ def test_ldb_x30
2779
+ assert_bytes [0xfe,0x7b,0xc1,0xa8] do |asm|
2780
+ asm.ldp x30, x30, [sp], 16
2781
+ end
2782
+ end
2783
+
2777
2784
  def test_LDP_gen
2778
2785
  # LDP <Wt1>, <Wt2>, [<Xn|SP>], #<imm>
2779
2786
  # LDP <Xt1>, <Xt2>, [<Xn|SP>], #<imm>
@@ -4649,22 +4656,21 @@ class BaseInstructionsTest < AArch64::Test
4649
4656
  end
4650
4657
 
4651
4658
  def test_MOVZ
4652
- asm.movz X0, 0x2a
4653
- assert_one_insn "movz x0, #0x2a"
4654
- # MOVZ
4655
- assert_bytes [0x02,0x00,0xa0,0x52] do |asm|
4656
- asm.movz w2, 0, lsl(16)
4659
+ assert_bytes [0x40, 0x5, 0x80, 0xd2] do |asm|
4660
+ asm.movz X0, 0x2a
4657
4661
  end
4658
4662
  end
4659
4663
 
4660
4664
  def test_movz_shift
4661
- asm.movz X0, 0x2a, lsl: 16
4662
- assert_one_insn "movz x0, #0x2a, lsl #16"
4665
+ assert_bytes [0x40, 0x5, 0xa0, 0xd2] do |asm|
4666
+ asm.movz X0, 0x2a, lsl: 16
4667
+ end
4663
4668
  end
4664
4669
 
4665
4670
  def test_movz_with_w
4666
- asm.movz W0, 0x2a
4667
- assert_one_insn "movz w0, #0x2a"
4671
+ assert_bytes [0x40, 0x5, 0x80, 82] do |asm|
4672
+ asm.movz W0, 0x2a
4673
+ end
4668
4674
  end
4669
4675
 
4670
4676
  def test_MRS
@@ -8866,6 +8872,18 @@ class BaseInstructionsTest < AArch64::Test
8866
8872
  asm.tbnz w3, 5, label
8867
8873
  asm.put_label label
8868
8874
  end
8875
+
8876
+ assert_one_insn "tbnz x0, #0x20, #4" do |asm|
8877
+ label = asm.make_label :foo
8878
+ asm.tbnz x0, 32, label
8879
+ asm.put_label label
8880
+ end
8881
+
8882
+ assert_one_insn "tbnz w0, #0, #4" do |asm|
8883
+ label = asm.make_label :foo
8884
+ asm.tbnz x0, 0, label
8885
+ asm.put_label label
8886
+ end
8869
8887
  end
8870
8888
 
8871
8889
  def test_TBZ
@@ -8878,6 +8896,12 @@ class BaseInstructionsTest < AArch64::Test
8878
8896
  asm.tbz w3, 5, label
8879
8897
  asm.put_label label
8880
8898
  end
8899
+
8900
+ assert_one_insn "tbz x0, #0x20, #4" do |asm|
8901
+ label = asm.make_label :foo
8902
+ asm.tbz x0, 32, label
8903
+ asm.put_label label
8904
+ end
8881
8905
  end
8882
8906
 
8883
8907
  def test_TLBI_SYS
data/test/dsl_test.rb CHANGED
@@ -1,6 +1,20 @@
1
1
  require "helper"
2
2
 
3
3
  class DSLTest < AArch64::Test
4
+ def test_patch_location
5
+ asm = AArch64::Assembler.new
6
+ found = nil
7
+ asm.pretty do
8
+ asm.mov x0, x1
9
+ asm.patch_location { |loc| found = loc }
10
+ asm.movz x2, 5
11
+ end
12
+
13
+ bytes = asm.to_binary
14
+ assert_equal 8, bytes.bytesize
15
+ assert_equal 4, found
16
+ end
17
+
4
18
  def test_dsl_has_methods
5
19
  assert_bytes [0x9f,0x08,0x25,0xab] do |asm|
6
20
  asm.pretty do
data/test/parser_test.rb CHANGED
@@ -2,12 +2,20 @@ require "helper"
2
2
  require "aarch64/parser"
3
3
 
4
4
  class ParserTest < AArch64::Test
5
+
6
+ def test_parse_error
7
+ error = assert_raises(Racc::ParseError) do
8
+ parse "mov x1, x0\nmov x1, #2 // comments not supported"
9
+ end
10
+ assert_equal 'parse error on value "/" (error) on line 2 at pos 23/48', error.message
11
+ end
12
+
5
13
  def test_parse
6
- assert_round_trip "movz x0, 0xCAFE", output: ["movz x0, #0xcafe"]
7
- assert_round_trip "movz x0, #0xcafe"
8
- assert_round_trip "movz x0, #0xcafe, lsl #16"
9
- assert_round_trip "movz w0, #0xcafe"
10
- assert_round_trip "movz w0, #0xcafe, lsl #16"
14
+ assert_bytes "movz x0, 0xCAFE", [0xc0, 0x5f, 0x99, 0xd2]
15
+ assert_bytes "movz x0, #0xcafe", [0xc0, 0x5f, 0x99, 0xd2]
16
+ assert_bytes "movz x0, #0xcafe, lsl #16", [0xc0, 0x5f, 0xb9, 0xd2]
17
+ assert_bytes "movz w0, #0xcafe", [0xc0, 0x5f, 0x99, 0x52]
18
+ assert_bytes "movz w0, #0xcafe, lsl #16", [0xc0, 0x5f, 0xb9, 0x52]
11
19
  end
12
20
 
13
21
  def test_autda
@@ -16,16 +24,20 @@ class ParserTest < AArch64::Test
16
24
  assert_bytes "autda x1, x2", [0x41, 0x18, 0xc1, 0xda]
17
25
  end
18
26
 
19
- def assert_bytes input, bytes
27
+ def parse input
20
28
  parser = AArch64::Parser.new
21
29
  begin
22
30
  asm = parser.parse input
23
31
  rescue Racc::ParseError
24
- puts input
25
32
  raise
26
33
  end
27
34
  io = StringIO.new
28
35
  asm.write_to io
36
+ io
37
+ end
38
+
39
+ def assert_bytes input, bytes
40
+ io = parse input
29
41
  assert_equal bytes, io.string.bytes, ->() {
30
42
  pos = 32.times.map { |i| (i % 0x10).to_s(16) }.join.reverse
31
43
  actual_bin = sprintf("%032b", io.string.unpack1("L<"))
@@ -42,10 +54,7 @@ class ParserTest < AArch64::Test
42
54
  end
43
55
 
44
56
  def assert_round_trip input, output: [input]
45
- parser = AArch64::Parser.new
46
- asm = parser.parse input
47
- io = StringIO.new
48
- asm.write_to io
57
+ io = parse input
49
58
  assert_equal output, disasm(io.string).map { |x|
50
59
  "#{x.mnemonic} #{x.op_str}"
51
60
  }
@@ -58,6 +67,85 @@ class ParserTest < AArch64::Test
58
67
  assert_bytes "add x8, x10, #1", [0x48, 0x5, 00, 0x91]
59
68
  end
60
69
 
70
+ def test_labels_already_defined
71
+ error = assert_raises(RuntimeError) do
72
+ parse "start:\nstart:\n"
73
+ end
74
+ assert_equal "symbol 'start' is already defined", error.message
75
+ end
76
+
77
+ def test_labels_empty
78
+ assert_bytes "start:\n", []
79
+ assert_bytes <<~ASM, []
80
+ start:
81
+ end:
82
+ ASM
83
+ end
84
+
85
+ def test_labels_not_defined
86
+ examples = [
87
+ "b label",
88
+ "b.eq label",
89
+ "b.lo label",
90
+ "b.lt label",
91
+ "b.hs label",
92
+ "b.gt label",
93
+ "b.le label",
94
+ "b.ne label",
95
+ "b.mi label",
96
+ "b.ge label",
97
+ "b.pl label",
98
+ "b.ls label",
99
+ "b.hi label",
100
+ "b.vc label",
101
+ "b.vs label",
102
+ "cbz x0, label",
103
+ "cbz w0, label",
104
+ "cbnz w0, label",
105
+ "cbnz x0, label",
106
+ "tbz w1, #1, label",
107
+ "tbz x1, #1, label",
108
+ "tbnz w1, #1, label",
109
+ "tbnz x1, #1, label",
110
+ "bl label",
111
+ ]
112
+ examples.each do |instruction|
113
+ error = assert_raises(RuntimeError) do
114
+ parse instruction
115
+ end
116
+ assert_match 'Label "label" not defined', error.message
117
+ end
118
+ end
119
+
120
+ def test_labels_branch
121
+ assert_bytes <<~ASM, [0x00, 0x04, 0x00, 0x91, 0xff, 0xff, 0xff, 0x17]
122
+ loop:
123
+ add x0, x0, #1
124
+ b loop
125
+ ASM
126
+ assert_bytes <<~ASM, [0x20, 0x00, 0x80, 0xd2, 0xe0, 0xff, 0xff, 0x10]
127
+ label:
128
+ mov x0, #1
129
+ adr x0, label
130
+ ASM
131
+ end
132
+
133
+ def test_labels_forward_reference
134
+ assert_bytes "b label\nlabel:", [0x01, 0x00, 0x00, 0x14]
135
+ assert_bytes "b.eq label\nlabel:", [0x20, 0x00, 0x00, 0x54]
136
+ assert_bytes "cbz w1, label\nlabel:", [0x21, 0x00, 0x00, 0x34]
137
+ assert_bytes "cbz x1, label\nlabel:", [0x21, 0x00, 0x00, 0xb4]
138
+ assert_bytes "cbnz w1, label\nlabel:", [0x21, 0x00, 0x00, 0x35]
139
+ assert_bytes "cbnz x1, label\nlabel:", [0x21, 0x00, 0x00, 0xb5]
140
+ assert_bytes "tbz w1, #1, label\nlabel:", [0x21, 0x00, 0x08, 0x36]
141
+ assert_bytes "tbz x1, #1, label\nlabel:", [0x21, 0x00, 0x08, 0x36]
142
+ assert_bytes "tbnz w1, #1, label\nlabel:", [0x21, 0x00, 0x08, 0x37]
143
+ assert_bytes "tbnz x1, #1, label\nlabel:", [0x21, 0x00, 0x08, 0x37]
144
+ assert_bytes "bl label\nlabel:", [0x01, 0x00, 0x00, 0x94]
145
+ assert_bytes "adr x0, label\nlabel:", [0x20, 0x00, 0x00, 0x10]
146
+ assert_bytes "adrp x0, label\nlabel:", [0x00, 0x00, 0x00, 0x90]
147
+ end
148
+
61
149
  def test_bls
62
150
  assert_bytes "b.ls #0x10", [0x89, 0x00, 0000, 0x54]
63
151
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aarch64
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Patterson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-19 00:00:00.000000000 Z
11
+ date: 2024-04-29 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: racc
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: hatstone
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -67,19 +81,19 @@ dependencies:
67
81
  - !ruby/object:Gem::Version
68
82
  version: '13.0'
69
83
  - !ruby/object:Gem::Dependency
70
- name: racc
84
+ name: odinflex
71
85
  requirement: !ruby/object:Gem::Requirement
72
86
  requirements:
73
87
  - - "~>"
74
88
  - !ruby/object:Gem::Version
75
- version: '1.6'
89
+ version: '1.0'
76
90
  type: :development
77
91
  prerelease: false
78
92
  version_requirements: !ruby/object:Gem::Requirement
79
93
  requirements:
80
94
  - - "~>"
81
95
  - !ruby/object:Gem::Version
82
- version: '1.6'
96
+ version: '1.0'
83
97
  description: Tired of writing Ruby in Ruby? Now you can write ARM64 assembly in Ruby!
84
98
  email: tenderlove@ruby-lang.org
85
99
  executables: []
@@ -380,7 +394,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
380
394
  - !ruby/object:Gem::Version
381
395
  version: '0'
382
396
  requirements: []
383
- rubygems_version: 3.5.0.dev
397
+ rubygems_version: 3.5.3
384
398
  signing_key:
385
399
  specification_version: 4
386
400
  summary: Write ARM64 assembly in Ruby!