-
Notifications
You must be signed in to change notification settings - Fork 0
/
xkon.hpp
3660 lines (3191 loc) · 125 KB
/
xkon.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#define _CRT_SECURE_NO_WARNINGS
/**
* 課題リスト
*
* * 絶対アドレス指定の call/tail がまともに動かない。
* おそらくアドレスの定義が定まってないため。
* ホストとRV32のアドレスがごっちゃになって破綻してる。
* 明確に型を定義して使用するように修正が必要。
* * 後方のラベルを参照する命令で、かつ圧縮命令が存在する場合
* 1パス目で相対アドレスの範囲が圧縮命令の範囲外でも圧縮命令を生成すると仮定してしまい、
* 1パス目で通常命令を生成してラベルのアドレスが変化してしまうケースがある。
* 2パス目でラベルのアドレス変化を検出して自動で3パス目を走らせることも考えたが、
* 3パス目でも同様のケースが起こったり、さらには何回パスを追加しても
* ラベルのアドレスが収束しないケースが無いことをすぐには証明できない。
* そのため、代替案としてラベルが圧縮命令で対応できない範囲に入る可能性がある場合
* 明示的にfar()で囲むことで圧縮命令の生成を抑止する実装として、
* ユーザーに問題の抑制を丸投げした。
* -> 命令生成lambdaに命令サイズ変化の可能性フラグを追加して、
* 2パス目でずれを検出したらリストをさかのぼりつつ必要な個数だけフラグ箇所にfar指定を強制追加してやり直す
* -> 2パス目で、ラベル自身でずれを検出して例外を飛ばすorASSERTで殺す
*
* 引数にラベルを指定可能で、圧縮命令になる可能性のある命令において、
* 命令より後方(アドレスが大きい)ラベルを引数に指定する場合、
* ラベルのアドレスとの距離が圧縮命令で表現可能な範囲を超えてしまう可能性がある場合は、
* ラベルをfar()で囲んで指定してください。
* この指定を誤ると、ラベルのアドレス決定処理でアドレスにずれが生じ、
* 正常に動作しない命令列を生成してしまいます。
* ※ラベルのアドレス決定処理では圧縮命令の生成に常に成功するが、
* 命令生成時は相対アドレスが遠い場合、アドレス決定時と異なり
* 通常命令が生成されれ、結果としてアドレスがずれる。
*
* * unsupportedの呼び出しがちゃんと入っているか?
* ->確認して入ってないところに入れた
* * ニーモニック生成処理を遅延評価で極力動作させないようにする
* ->準備だけはした。ニーモニック出力のON/OFF機能は未実装
*/
#include <algorithm>
#include <cstdint>
#include <exception>
#include <functional>
#include <list>
#include <map>
#include <string>
#include "bitbuilder.hpp"
#if 0
void dummy(const char* what) { throw std::runtime_error(what); }
#define XKON_ASSERT(expr) \
do { \
if (!(expr)) { \
dummy(#expr); \
} \
} while (0)
#else
#define XKON_ASSERT(expr) assert(expr)
#endif
#if not +0
// and or not を関数名として使用できる設定になっているか、
// 本家と同じ手法でエラー判定する
#error "use -fno-operator-names"
#define XKON_OPERATER_NAMES_ARE_USEABLE 0
#else
#define XKON_OPERATER_NAMES_ARE_USEABLE 1
#endif
#define XKON_INSN_NAME(x) x
#define XKON_LAZY(expr) [&]() -> const Format { return (expr); }
//#define XKON_LAZY(expr) [=]()->const Format{ return Format("x"); }
// 開墾
namespace xkon {
typedef uint32_t uint32;
typedef int32_t int32;
typedef unsigned long long addr_t;
typedef signed long long addrdiff_t;
using namespace BitBuilder;
/**
* 命令セットアーキテクチャ
*
* misaレジスタ風に、対応している拡張とCPUのビット数のビットマップ
*/
enum Isa {
// Extensions
EXT_I = 0x00000100,
// EXT_E = 0x00000010,
EXT_M = 0x00001000,
EXT_A = 0x00000001,
EXT_F = 0x00000020,
EXT_D = 0x00000008,
// EXT_Q = 0x00010000,
// EXT_L = 0x00000800,
EXT_C = 0x00000004,
// EXT_B = 0x00000002,
// EXT_J = 0x00000200,
// EXT_T = 0x00080000,
// EXT_P = 0x00008000,
// EXT_V = 0x00200000,
// EXT_N = 0x00002000,
EXT_G = 0x00000040 | EXT_I | EXT_M | EXT_A | EXT_F | EXT_D,
// Bases
RV32 = 0x04000000,
RV64 = 0x08000000,
RV128 = 0x10000000,
// Major variations
RV32I = RV32 | EXT_I,
RV32IC = RV32 | EXT_I | EXT_C,
RV32IMA = RV32 | EXT_I | EXT_M | EXT_A,
RV32G = RV32 | EXT_G,
RV32GC = RV32 | EXT_G | EXT_C,
RV64I = RV64 | EXT_I,
};
////////////////////////////////////////////////////////////////////////////////
// 例外
/// 「未サポート」例外クラス
/// 指定されたISAでは対応していない命令の生成を試みた際に発生する例外
class UnsupportedException : public std::exception {
std::string m_what;
public:
UnsupportedException(const std::string& what) noexcept : exception(), m_what(what) {}
UnsupportedException(const std::exception& e) noexcept : exception(e), m_what("") {}
UnsupportedException(const UnsupportedException& o) noexcept { this->m_what = o.m_what; }
virtual ~UnsupportedException() {}
UnsupportedException& operator=(const UnsupportedException&) noexcept = default;
virtual const char* what() const noexcept { return m_what.c_str(); }
};
////////////////////////////////////////////////////////////////////////////////
// ラベル
////////////////////////////////////////////////////////////////////////////////
// レジスタ
/// レジスタ名を整数にエンコードする
constexpr unsigned long enc(const char* s) {
unsigned int v = 0;
for (; *s != '\0'; s++) {
v = (v << 8) | (*s & 0xff);
}
return v;
}
/// enc()でエンコードされた整数を文字列に戻す
std::string dec(unsigned long v) {
char buf[5] = {0};
int i = 0;
if (v & 0xff000000u) {
buf[i++] = (v >> 24) & 0xff;
}
if (v & 0x00ff0000u) {
buf[i++] = (v >> 16) & 0xff;
}
if (v & 0x0000ff00u) {
buf[i++] = (v >> 8) & 0xff;
}
if (v & 0x000000ffu) {
buf[i++] = (v >> 0) & 0xff;
}
return std::string(buf);
}
/**
* レジスタ用変数から暗黙の型変換で変換されるクラス
*/
struct RegBase {
const int idx; /**< レジスタのインデックス番号 */
const int cidx; /**< 圧縮命令用のレジスタのインデックス番号*/
const std::string name;
RegBase(int idx, int cidx, unsigned long name) : idx(idx), cidx(cidx), name(dec(name)) {}
bool isC() const { return 0 <= cidx; }
Constant Idx() const { return Constant(5, idx); }
Constant CIdx() const {
XKON_ASSERT(0 <= cidx);
return Constant(3, cidx);
}
};
// 整数レジスタ
struct IntOffsetReg;
struct IntReg : public RegBase {
IntReg(int idx, int cidx, unsigned long name) : RegBase(idx, cidx, name) {}
bool operator==(const IntReg& o) const { return this->idx == o.idx; }
bool operator!=(const IntReg& o) const { return this->idx != o.idx; }
IntOffsetReg operator()(long offset = 0) const;
};
struct IntOffsetReg : public IntReg {
const long offset;
IntOffsetReg(long offset, int idx, int cidx, unsigned long name) : IntReg(idx, cidx, name), offset(offset) {}
};
IntOffsetReg IntReg::operator()(long offset) const {
IntOffsetReg res(offset, idx, cidx, enc(name.c_str()));
return res;
}
template <int id, unsigned long name = 0, int cid = -1>
struct IReg {
operator IntReg() const {
using namespace std;
return IntReg(id, cid, (name == 0) ? enc(("x"s + to_string(id)).c_str()) : name);
}
operator IntOffsetReg() const {
using namespace std;
return IntOffsetReg(0, id, cid, (name == 0) ? enc(("x"s + to_string(id)).c_str()) : name);
}
IntOffsetReg operator()(long offset = 0) const {
using namespace std;
return IntOffsetReg(offset, id, cid, (name == 0) ? enc(("x"s + to_string(id)).c_str()) : name);
}
IntOffsetReg operator[](long offset) const {
using namespace std;
return IntOffsetReg(offset, id, cid, (name == 0) ? enc(("x"s + to_string(id)).c_str()) : name);
}
}; // namespace xkon
// 浮動小数点数レジスタ
struct FpReg : public RegBase {
FpReg(int idx, int cidx, unsigned long name) : RegBase(idx, cidx, name) {}
bool operator==(const FpReg& o) const { return this->idx == o.idx; }
bool operator!=(const FpReg& o) const { return this->idx != o.idx; }
};
template <int id, unsigned long name = 0, int cid = -1>
struct FReg {
operator FpReg() const {
using namespace std;
return FpReg(id, cid, (name == 0) ? enc(("f"s + to_string(id)).c_str()) : name);
}
};
////////////////////////////////////////////////////////////////////////////////
// コード生成クラスの定義
/**
* RISC-V の仕様上定義されているレジスタを表す変数を定義したクラス
*/
struct Registers {
// 整数レジスタの定義
static const IReg<0, enc("zero")> x0, zero; // ---
static const IReg<1, enc("ra")> x1, ra; // caller
static const IReg<2, enc("sp")> x2, sp; // CALLEE
static const IReg<3, enc("gp")> x3, gp; // ---
static const IReg<4, enc("tp")> x4, tp; // ---
static const IReg<5, enc("t0")> x5, t0; // caller
static const IReg<6, enc("t1")> x6, t1; // caller
static const IReg<7, enc("t2")> x7, t2; // caller
static const IReg<8, enc("s0"), 0> x8, s0, fp; // CALLEE
static const IReg<9, enc("s1"), 1> x9, s1; // CALLEE
static const IReg<10, enc("a0"), 2> x10, a0; // caller
static const IReg<11, enc("a1"), 3> x11, a1; // caller
static const IReg<12, enc("a2"), 4> x12, a2; // caller
static const IReg<13, enc("a3"), 5> x13, a3; // caller
static const IReg<14, enc("a4"), 6> x14, a4; // caller
static const IReg<15, enc("a5"), 7> x15, a5; // caller
static const IReg<16, enc("a6")> x16, a6; // caller
static const IReg<17, enc("a7")> x17, a7; // caller
static const IReg<18, enc("s2")> x18, s2; // CALLEE
static const IReg<19, enc("s3")> x19, s3; // CALLEE
static const IReg<20, enc("s4")> x20, s4; // CALLEE
static const IReg<21, enc("s5")> x21, s5; // CALLEE
static const IReg<22, enc("s6")> x22, s6; // CALLEE
static const IReg<23, enc("s7")> x23, s7; // CALLEE
static const IReg<24, enc("s8")> x24, s8; // CALLEE
static const IReg<25, enc("s9")> x25, s9; // CALLEE
static const IReg<26, enc("s10")> x26, s10; // CALLEE
static const IReg<27, enc("s11")> x27, s11; // CALLEE
static const IReg<28, enc("t3")> x28, t3; // caller
static const IReg<29, enc("t4")> x29, t4; // caller
static const IReg<30, enc("t5")> x30, t5; // caller
static const IReg<31, enc("t6")> x31, t6; // caller
// 浮動小数点数レジスタの定義
static const FReg<0, enc("ft0")> f0, ft0; // caller
static const FReg<1, enc("ft1")> f1, ft1; // caller
static const FReg<2, enc("ft2")> f2, ft2; // caller
static const FReg<3, enc("ft3")> f3, ft3; // caller
static const FReg<4, enc("ft4")> f4, ft4; // caller
static const FReg<5, enc("ft5")> f5, ft5; // caller
static const FReg<6, enc("ft6")> f6, ft6; // caller
static const FReg<7, enc("ft7")> f7, ft7; // caller
static const FReg<8, enc("fs0"), 0> f8, fs0; // CALLEE
static const FReg<9, enc("fs1"), 1> f9, fs1; // CALLEE
static const FReg<10, enc("fa0"), 2> f10, fa0; // caller
static const FReg<11, enc("fa1"), 3> f11, fa1; // caller
static const FReg<12, enc("fa2"), 4> f12, fa2; // caller
static const FReg<13, enc("fa3"), 5> f13, fa3; // caller
static const FReg<14, enc("fa4"), 6> f14, fa4; // caller
static const FReg<15, enc("fa5"), 7> f15, fa5; // caller
static const FReg<16, enc("fa6")> f16, fa6; // caller
static const FReg<17, enc("fa7")> f17, fa7; // caller
static const FReg<18, enc("fs2")> f18, fs2; // CALLEE
static const FReg<19, enc("fs3")> f19, fs3; // CALLEE
static const FReg<20, enc("fs4")> f20, fs4; // CALLEE
static const FReg<21, enc("fs5")> f21, fs5; // CALLEE
static const FReg<22, enc("fs6")> f22, fs6; // CALLEE
static const FReg<23, enc("fs7")> f23, fs7; // CALLEE
static const FReg<24, enc("fs8")> f24, fs8; // CALLEE
static const FReg<25, enc("fs9")> f25, fs9; // CALLEE
static const FReg<26, enc("fs10")> f26, fs10; // CALLEE
static const FReg<27, enc("fs11")> f27, fs11; // CALLEE
static const FReg<28, enc("ft8")> f28, ft8; // caller
static const FReg<29, enc("ft9")> f29, ft9; // caller
static const FReg<30, enc("ft10")> f30, ft10; // caller
static const FReg<31, enc("ft11")> f31, ft11; // caller
};
// レジスタ用変数のインスタンス化
// gccでは Registers クラスのインスタンスを用意すればOKだったが、
// MSVCでは各staticメンバー変数の実体を明示的に用意しないと
// リンク時にエラーが発生したので 明示的に実体を用意する
#define XKON_REGDEF(reg) decltype(Registers::reg) Registers::reg
XKON_REGDEF(x0);
XKON_REGDEF(zero);
XKON_REGDEF(x1);
XKON_REGDEF(ra);
XKON_REGDEF(x2);
XKON_REGDEF(sp);
XKON_REGDEF(x3);
XKON_REGDEF(gp);
XKON_REGDEF(x4);
XKON_REGDEF(tp);
XKON_REGDEF(x5);
XKON_REGDEF(t0);
XKON_REGDEF(x6);
XKON_REGDEF(t1);
XKON_REGDEF(x7);
XKON_REGDEF(t2);
XKON_REGDEF(x8);
XKON_REGDEF(s0);
XKON_REGDEF(fp);
XKON_REGDEF(x9);
XKON_REGDEF(s1);
XKON_REGDEF(x10);
XKON_REGDEF(a0);
XKON_REGDEF(x11);
XKON_REGDEF(a1);
XKON_REGDEF(x12);
XKON_REGDEF(a2);
XKON_REGDEF(x13);
XKON_REGDEF(a3);
XKON_REGDEF(x14);
XKON_REGDEF(a4);
XKON_REGDEF(x15);
XKON_REGDEF(a5);
XKON_REGDEF(x16);
XKON_REGDEF(a6);
XKON_REGDEF(x17);
XKON_REGDEF(a7);
XKON_REGDEF(x18);
XKON_REGDEF(s2);
XKON_REGDEF(x19);
XKON_REGDEF(s3);
XKON_REGDEF(x20);
XKON_REGDEF(s4);
XKON_REGDEF(x21);
XKON_REGDEF(s5);
XKON_REGDEF(x22);
XKON_REGDEF(s6);
XKON_REGDEF(x23);
XKON_REGDEF(s7);
XKON_REGDEF(x24);
XKON_REGDEF(s8);
XKON_REGDEF(x25);
XKON_REGDEF(s9);
XKON_REGDEF(x26);
XKON_REGDEF(s10);
XKON_REGDEF(x27);
XKON_REGDEF(s11);
XKON_REGDEF(x28);
XKON_REGDEF(t3);
XKON_REGDEF(x29);
XKON_REGDEF(t4);
XKON_REGDEF(x30);
XKON_REGDEF(t5);
XKON_REGDEF(x31);
XKON_REGDEF(t6);
XKON_REGDEF(f0);
XKON_REGDEF(ft0);
XKON_REGDEF(f1);
XKON_REGDEF(ft1);
XKON_REGDEF(f2);
XKON_REGDEF(ft2);
XKON_REGDEF(f3);
XKON_REGDEF(ft3);
XKON_REGDEF(f4);
XKON_REGDEF(ft4);
XKON_REGDEF(f5);
XKON_REGDEF(ft5);
XKON_REGDEF(f6);
XKON_REGDEF(ft6);
XKON_REGDEF(f7);
XKON_REGDEF(ft7);
XKON_REGDEF(f8);
XKON_REGDEF(fs0);
XKON_REGDEF(f9);
XKON_REGDEF(fs1);
XKON_REGDEF(f10);
XKON_REGDEF(fa0);
XKON_REGDEF(f11);
XKON_REGDEF(fa1);
XKON_REGDEF(f12);
XKON_REGDEF(fa2);
XKON_REGDEF(f13);
XKON_REGDEF(fa3);
XKON_REGDEF(f14);
XKON_REGDEF(fa4);
XKON_REGDEF(f15);
XKON_REGDEF(fa5);
XKON_REGDEF(f16);
XKON_REGDEF(fa6);
XKON_REGDEF(f17);
XKON_REGDEF(fa7);
XKON_REGDEF(f18);
XKON_REGDEF(fs2);
XKON_REGDEF(f19);
XKON_REGDEF(fs3);
XKON_REGDEF(f20);
XKON_REGDEF(fs4);
XKON_REGDEF(f21);
XKON_REGDEF(fs5);
XKON_REGDEF(f22);
XKON_REGDEF(fs6);
XKON_REGDEF(f23);
XKON_REGDEF(fs7);
XKON_REGDEF(f24);
XKON_REGDEF(fs8);
XKON_REGDEF(f25);
XKON_REGDEF(fs9);
XKON_REGDEF(f26);
XKON_REGDEF(fs10);
XKON_REGDEF(f27);
XKON_REGDEF(fs11);
XKON_REGDEF(f28);
XKON_REGDEF(ft8);
XKON_REGDEF(f29);
XKON_REGDEF(ft9);
XKON_REGDEF(f30);
XKON_REGDEF(ft10);
XKON_REGDEF(f31);
XKON_REGDEF(ft11);
#undef XKON_REGDEF
class Allocator {
// 根本の原因は判らないが、spike で動作確認を行っていると
// メモリに書き込んだ命令をうまく読みだせず落ちる。
// 試行錯誤した結果、new したメモリ領域から2048バイトにアライメント
// したメモリを使用するとうまく動いたので対症療法として
// アライメント処理を追加した。
const size_t ALIGN = 2048;
size_t size;
void* ptr;
bool self_allocated; // メモリ確保を自前でやったフラグ
char* pMem;
public:
Allocator() : size(0), ptr(nullptr), self_allocated(false), pMem(nullptr) {}
virtual ~Allocator() {
if (self_allocated) {
delete[](unsigned char*) this->ptr;
}
}
void allocate(size_t size, void* ptr) {
this->size = size;
this->ptr = ptr;
if (this->ptr == nullptr) {
this->ptr = new unsigned char[size + ALIGN];
self_allocated = true;
// アライメント調整
intptr_t p = (intptr_t)this->ptr;
p = (p + ALIGN - 1) & (~(ALIGN - 1));
this->pMem = (char*)p;
} else {
this->pMem = (char*)ptr;
}
assert(this->ptr != nullptr);
assert(this->pMem != nullptr);
}
char* getMemory() const { return pMem; }
size_t getSize() const { return size; }
}; // namespace internal
class Strage;
// nameが空文字列の場合は、絶対アドレスaddressを指すための相対アドレスを表し、
// それ以外の場合はラベルで指定された相対アドレスを指す
struct Label {
const Strage* pS;
const std::string name;
const addr_t address;
const bool isFar; ///< 圧縮命令で届かないアドレスであること明示するためのフラグ
Label(const Strage* pS, const char* name, bool isFar = false) : pS(pS), name(name), address(0), isFar(isFar) {}
public:
// ラベル指定用コンストラクタ
Label(Strage& strage, const char* name, bool isFar = false) : pS(&strage), name(name), address(0), isFar(isFar) {}
// アドレス指定用コンストラクタ
Label(Strage& strage, addr_t address) : pS(&strage), name(""), address(address), isFar(false) {}
addrdiff_t relAddr() const; // Strageクラスの関数を呼ぶので後方で定義
addr_t absAddr() const; // Strageクラスの関数を呼ぶので後方で定義
// 確保したメモリー領域の先頭からのオフセット
addr_t value() const; // Strageクラスの関数を呼ぶので後方で定義
bool isNear() const { return !isFar; }
Label toFar() const { return Label(pS, name.c_str(), true); }
std::string dump() const {
addrdiff_t addr = relAddr();
if (name.empty()) {
char buf[64];
snprintf(buf, sizeof(buf) - 1, "0x%llx", addr);
return std::string(buf);
} else {
char sign = (0 <= addr) ? '+' : '-';
if (addr < 0) {
addr = -addr;
}
char buf[64];
snprintf(buf, sizeof(buf) - 1, ":pc%c0x%llx", sign, addr);
return name + buf;
}
}
};
/**
* 命令のニーモニック表記文字列生成用フォーマットクラス
*/
class Format {
/// フォーマット済み文字列
const std::string str;
/// 未処理のフォーマット指定文字列
const std::string format;
const enum Type {
TypeOp = 'o', ///< 命令
TypeIReg = 'i', ///<整数レジスタ
TypeIOReg = 'I', ///<オフセット値付き整数レジスタ
TypeIJReg = 'J', ///<オフセット値付き整数レジスタ(jalr用)
TypeIMReg = 'M', ///<オフセット値付き整数レジスタ(メモリ)
TypeFReg = 'f', ///<浮動小数点数レジスタ
TypeSimm = 's', ///<符号有り即値
TypeUimm = 'u', ///<符号なし即値
TypeRM = 'r', ///<丸めモード
TypeLabel = 'L', ///<ラベル文字列
TypeRem = '#' ///<注釈コメント
} type;
Format(const std::string& str, const std::string& format) //
: str(str), format(&format[1]), type(static_cast<enum Type>(format[0])) {}
std::string sep() const {
using namespace std;
if (str.empty() || str.back() == ' ') {
return ""s;
} else {
return ","s;
}
}
public:
Format(const char* format) : str(), format(format[0] ? &format[1] : ""), type(static_cast<enum Type>(format[0])) {}
Format operator%(const IntOffsetReg& r) const {
using namespace std;
XKON_ASSERT(type == TypeIReg || type == TypeIOReg || type == TypeIJReg || type == TypeIMReg);
switch (type) {
case TypeIReg:
return Format(str + sep() + r.name, format);
case TypeIOReg:
return Format(str + sep() + std::to_string(r.offset) + "("s + r.name + ")"s, format);
break;
case TypeIMReg:
XKON_ASSERT(r.offset == 0);
return Format(str + sep() + "("s + r.name + ")"s, format);
case TypeIJReg:
if (r.offset == 0) {
return Format(str + sep() + r.name, format);
} else {
return Format(str + sep() + std::to_string(r.offset) + "("s + r.name + ")"s, format);
}
break;
default:
XKON_ASSERT(0);
break;
}
XKON_ASSERT(0);
return Format("");
}
Format operator%(const IntReg& r) const {
using namespace std;
XKON_ASSERT(type == TypeIReg || type == TypeIOReg);
switch (type) {
case TypeIReg:
return Format(str + sep() + r.name, format);
default:
XKON_ASSERT(0);
break;
}
XKON_ASSERT(0);
return Format("");
}
Format operator%(const FpReg& f) const {
XKON_ASSERT(type == TypeFReg);
return Format(str + sep() + f.name, format);
}
static const char* rm2s(int rm) {
const char* name = nullptr;
switch (rm) {
case 0:
name = "rne";
break;
case 1:
name = "rtz";
break;
case 2:
name = "rdn";
break;
case 3:
name = "rup";
break;
case 4:
name = "rmm";
break;
case 7:
// name = "dyn";
break;
default:
XKON_ASSERT(0);
break;
}
return name;
}
Format operator%(int imm) const {
XKON_ASSERT(type == TypeSimm || type == TypeUimm || type == TypeRM);
char buf[64] = {0};
switch (type) {
case TypeSimm:
std::snprintf(buf, sizeof(buf) - 1, "%d", imm);
break;
case TypeUimm:
std::snprintf(buf, sizeof(buf) - 1, "0x%x", imm);
break;
case TypeRM: {
const char* sym = rm2s(imm);
if (sym == nullptr) {
return Format(str, format);
}
std::snprintf(buf, sizeof(buf) - 1, "%s", sym);
break;
}
default:
XKON_ASSERT(0);
break;
}
return Format(str + sep() + buf, format);
}
Format operator%(long imm) const { return (*this) % static_cast<int>(imm); }
Format operator%(unsigned long imm) const { return (*this) % static_cast<int>(imm); }
Format operator%(unsigned int imm) const { return (*this) % static_cast<int>(imm); }
Format operator%(const std::string& s) const { return (*this) % (s.c_str()); }
Format operator%(const Label& l) const {
// DEBUG
if (true) {
XKON_ASSERT(type == TypeLabel);
char buf[256];
snprintf(buf, sizeof(buf) - 1, "%llx <%s>", l.value(), l.name.c_str());
return Format(str + sep() + buf, format);
} else {
return (*this) % (l.dump().c_str());
}
}
Format operator%(const char* s) const {
using namespace std;
XKON_ASSERT(type == TypeOp || type == TypeRem || type == TypeLabel);
switch (type) {
case TypeOp:
return Format(str + s + " "s, format);
case TypeRem:
return Format(str + "\t# "s + s + " "s, format);
case TypeLabel:
return Format(str + sep() + "<"s + s + ">"s, format);
default:
break;
}
XKON_ASSERT(0);
return Format("");
}
operator std::string() const { return str; }
}; // namespace xkon
class Strage {
inline Strage(const Strage&) = delete;
typedef std::function<void(Strage&)> InsnGen_t;
Allocator mem;
// ラベル管理
std::map<std::string, addr_t> labelMap;
// 命令の生成管理
addr_t p; ///< メモリーの書込み位置インデックス
addr_t pc; ///< 現在の処理中の命令の先頭アドレス
std::list<InsnGen_t> insns; ///< 命令生成関数のリスト
bool inGenerate; ///< false:insnsへの命令生成lambda式追加とラベルのアドレス決定モード true:命令生成モード
unsigned int lastInsn; ///< 最後に生成した命令のopコード
FILE* fp; // DEBUG
public:
Strage(std::size_t size) : mem(), labelMap(), p(0), pc(0), insns(), inGenerate(false), lastInsn(), fp(nullptr) { mem.allocate(size, nullptr); }
void operator<<(InsnGen_t ig) {
ig(*this);
pc = p;
insns.push_back(ig);
}
// コード生成
char* generate() {
// DEBUG
fp = fopen("out.s", "w");
fprintf(fp, "%s",
"\t.file \"out.s\"\n"
"\t.option nopic\n"
"\t.text\n"
"\t.align 1\n"
"\t.globl f\n"
"\t.type f, @function\n"
"f:\n");
// 変数の初期化
p = 0;
pc = 0;
inGenerate = true;
int count = 0; ///< デバッグメッセージ用の文字列出力タイミング制御カウンタ
// 命令生成メインループ
for (auto e : insns) {
// デバッグメッセージ用の文字列出力
if (count++ == 0) {
#if DEBUG
printf("%s", "Address OPcode ------- Instruction --------------------------------------------\n");
#endif
} else if (16 <= count) {
count = 0;
}
// 命令生成用のlambda式の実行
e(*this);
// PCを更新
pc = p;
}
inGenerate = false;
#if DEBUG
printf("%llu bytes generated.\n", p);
#endif
// DEBUG
fprintf(fp, "%s",
"" //
// "\tnop\n"
// "\tnop\n"
// "\tfmul.s ft0,ft1,ft2\n"
// "\tfmul.s ft0,ft1,ft2,rne\n"
// "\tfmul.s ft0,ft1,ft2,rtz\n"
// "\tfmul.s ft0,ft1,ft2,rdn\n"
// "\tfmul.s ft0,ft1,ft2,rup\n"
// "\tfmul.s ft0,ft1,ft2,rmm\n"
// "\tfmul.s ft0,ft1,ft2,dyn\n"
//
);
fclose(fp);
return mem.getMemory();
}
// 命令出力
void hword(unsigned int ui16) {
XKON_ASSERT(p + 2 <= mem.getSize());
if (inGenerate) {
char* pMem = mem.getMemory();
lastInsn = ui16;
pMem[p++] = ui16 & 0xff;
pMem[p++] = (ui16 >> 8) & 0xff;
} else {
p += 2;
}
}
void word(unsigned int ui32) {
XKON_ASSERT(p + 4 <= mem.getSize());
if (inGenerate) {
char* pMem = mem.getMemory();
lastInsn = ui32;
pMem[p++] = ui32 & 0xff;
pMem[p++] = (ui32 >> 8) & 0xff;
pMem[p++] = (ui32 >> 16) & 0xff;
pMem[p++] = (ui32 >> 24) & 0xff;
} else {
p += 4;
}
}
// 命令のテキスト表記出力
Format format(const char* format) const { return Format(format); }
typedef std::function<const Format(void)> lazy_expr_t;
// ニーモニック出力を無効に出来るようにした際に余計な処理を実行しないようにするためラムダ式を使った遅延評価を行う構造にした
void desc(lazy_expr_t fs) {
if (!inGenerate) {
return;
}
std::string s = fs();
// RISC-Vの32ビットサイズの命令は必ず下位ビットが1、それ以外の場合は16ビットサイズの命令なので、それを利用して命令のビット数を判定する
if ((lastInsn & 3) == 3) {
#if DEBUG
std::printf("%llx:\t\x1b[35m%08x\t\x1b[36m%s\x1b[0m\n", pc, lastInsn, s.c_str());
#endif
if (fp != nullptr) { // DEBUG
std::fprintf(fp, "\t.word 0x%08x\t#%s\n", lastInsn, s.c_str());
}
} else {
#if DEBUG
std::printf("%llx:\t\x1b[35m %04x\t\x1b[36m%s\x1b[0m\n", pc, lastInsn, s.c_str());
#endif
if (fp != nullptr) { // DEBUG
std::fprintf(fp, "\t.hword 0x%04x\t#%s\n", lastInsn, s.c_str());
}
}
}
// 1つの命令の処理中で複数の命令を出力する場合に、PCを強制的に更新するために使う
void updatePC() { pc = p; }
// ラベル管理
void addLabel(const char* label) {
if (inGenerate) {
#if DEBUG
printf("\x1b[36m%08llx <%s>\x1b[0m:\n", labelMap[std::string(label)], label);
#endif
if (fp != nullptr) { // DEBUG
std::fprintf(fp, "%s:\n", label);
}
} else {
#if DEBUG
printf("+0x%llx = <%s>\n", pc, label);
#endif
labelMap[std::string(label)] = pc;
}
}
addrdiff_t getLabelOffset(const char* label) const {
const auto itr = labelMap.find(std::string(label));
if (itr == labelMap.end()) {
if (inGenerate) {
throw UnsupportedException("Unknown label.");
} else {
return 0;
}
} else {
if (itr->second > pc) {
return itr->second - pc;
} else {
return -(pc - itr->second);
}
}
}
addr_t getLabelValue(const char* label) const {
const auto itr = labelMap.find(std::string(label));
if (itr == labelMap.end()) {
if (inGenerate) {
throw UnsupportedException("Unknown label.");
} else {
return 0;
}
} else {
return itr->second;
}
}
addrdiff_t getLabelOffset(const std::string& label) const { return getLabelOffset(label.c_str()); }
addr_t getLabelValue(const std::string& label) const { return getLabelValue(label.c_str()); }
addr_t getPC() const { return pc + (intptr_t)mem.getMemory(); }
};
addrdiff_t Label::relAddr() const {
if (name.empty()) {
if (address > pS->getPC()) {
return address - pS->getPC();
} else {
return -(pS->getPC() - address);
}
} else {
return pS->getLabelOffset(name);
}
}
addr_t Label::absAddr() const {
if (name.empty()) {
return address;
} else {
return pS->getLabelOffset(name) + pS->getPC();
}
}
addr_t Label::value() const { return pS->getLabelValue(name); }
/*******************************************************************************
* コード生成クラス
******************************************************************************/
template <Isa support_isa>
class CodeGenerator : public Registers {
typedef CodeGenerator<support_isa> self_t;
Strage st;
//////////////////////////////////////////////////////////////////////////////
// 内部実装用関数の定義
// テンプレート引数の support_ias が、 require で示される機能を
// すべてサポートしている(=ターゲットである)か判定して返す
template <int require>
static inline constexpr bool targetIs() {
return (support_isa & require) == require;
}
// 「命令未サポート例外」を発生させる関数
// 引数には __func__ を指定する想定なので、
// 例外メッセージに実際の命令の名前を表示するため、関数名から実際の命令の名前に変換を行う
// 関数名は、命令の名前のドットを下線に置換し、名前がC++定義済みの演算子と被る場合はさらに末尾に下線を付加した名前にしている。
static void unsupported(const char* insn_name) {
using namespace std;
// 関数名→命令の名前変換処理
string s(insn_name);
replace(s.begin(), s.end(), '_', '.');
if ((!s.empty()) && s.back() == '.') {
s.pop_back();
}
const string what = "'"s + s + "' instruction not supported."s;
throw UnsupportedException(what);
}
//////////////////////////////////////////////////////////////////////////////
public:
/// 丸めモード
enum RoundingMode {
rne = 0, ///<最近の偶数へ丸める
rtz = 1, ///<ゼロに向かって丸める
rdn = 2, ///<切り下げ(-∞方向)
rup = 3, ///<切り上げ(+∞方向)
rmm = 4, ///<最も近い絶対値が大きい方向に丸める
invalid_rounding_mode5 = 5, ///< 不正な値。将来使用するため予約。
invalid_rounding_mode6 = 6, ///< 不正な値。将来使用するため予約。
dyn = 7, ///<動的丸めモード(丸めモードレジスタでは使用できない値)
};
static Constant from(RoundingMode rm) { return Constant(3, rm); }