aarch64 2.0.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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!