1use proc_macro::TokenStream;
2use proc_macro2::TokenTree;
3use quote::quote;
4
5const ASM_OPERAND_KEYWORDS: &[&str] = &[
9 "in",
10 "out",
11 "inout",
12 "lateout",
13 "inlateout",
14 "const",
15 "sym",
16 "options",
17 "clobber_abi",
18];
19
20#[proc_macro]
59pub fn kv_asm(input: TokenStream) -> TokenStream {
60 let input2: proc_macro2::TokenStream = input.into();
61 let tokens: Vec<TokenTree> = input2.into_iter().collect();
62
63 if tokens.is_empty() {
64 return quote! {
65 unsafe {
66 core::arch::asm!("", options(raw))
67 }
68 }
69 .into();
70 }
71
72 let (asm_tokens, operand_tokens) = split_asm_and_operands(&tokens);
73
74 let lines = group_tokens_by_line(&asm_tokens);
75 let asm_strings: Vec<String> = lines
76 .into_iter()
77 .map(|line_tokens| format_instruction(&line_tokens))
78 .filter(|s: &String| !s.is_empty())
79 .collect();
80
81 let operands: proc_macro2::TokenStream = operand_tokens.into_iter().cloned().collect();
82
83 let template = asm_strings.join("\n");
84 let template_lit = proc_macro2::Literal::string(&template);
85
86 let output = if operands.is_empty() {
87 quote! {
88 unsafe {
89 core::arch::asm!(
90 #template_lit,
91 options(raw)
92 )
93 }
94 }
95 } else {
96 quote! {
97 unsafe {
98 core::arch::asm!(
99 #template_lit,
100 #operands
101 options(raw)
102 )
103 }
104 }
105 };
106
107 output.into()
108}
109
110#[proc_macro]
131pub fn kv_global_asm(input: TokenStream) -> TokenStream {
132 let input2: proc_macro2::TokenStream = input.into();
133 let tokens: Vec<TokenTree> = input2.into_iter().collect();
134
135 if tokens.is_empty() {
136 return quote! { core::arch::global_asm!(""); }.into();
137 }
138
139 let (asm_tokens, operand_tokens) = split_asm_and_operands(&tokens);
140
141 let lines = group_tokens_by_line(&asm_tokens);
142 let asm_strings: Vec<String> = lines
143 .into_iter()
144 .map(|line_tokens| format_instruction(&line_tokens))
145 .filter(|s: &String| !s.is_empty())
146 .collect();
147
148 let operands: proc_macro2::TokenStream = operand_tokens.into_iter().cloned().collect();
149
150 let template = asm_strings.join("\n");
151 let template_lit = proc_macro2::Literal::string(&template);
152
153 let output = if operands.is_empty() {
154 quote! {
155 core::arch::global_asm!(
156 #template_lit,
157 options(raw)
158 );
159 }
160 } else {
161 quote! {
162 core::arch::global_asm!(
163 #template_lit,
164 #operands
165 );
166 }
167 };
168
169 output.into()
170}
171
172#[proc_macro]
184pub fn kv_asm_array(input: TokenStream) -> TokenStream {
185 let input2: proc_macro2::TokenStream = input.into();
186 let tokens: Vec<TokenTree> = input2.into_iter().collect();
187
188 if tokens.is_empty() {
189 return quote! { &[] as &[&str] }.into();
190 }
191
192 let (asm_tokens, _) = split_asm_and_operands(&tokens);
193
194 let lines = group_tokens_by_line(&asm_tokens);
195 let asm_strings: Vec<String> = lines
196 .into_iter()
197 .map(|line_tokens| format_instruction(&line_tokens))
198 .filter(|s: &String| !s.is_empty())
199 .collect();
200
201 quote! {
202 &[#(#asm_strings),*]
203 }
204 .into()
205}
206
207fn is_asm_operand_keyword(token: &TokenTree) -> bool {
208 if let TokenTree::Ident(ident) = token {
209 let s = ident.to_string();
210 ASM_OPERAND_KEYWORDS.contains(&s.as_str())
211 } else {
212 false
213 }
214}
215
216fn is_bpf_plain_reg_ident(ident: &proc_macro2::Ident) -> bool {
219 let s = ident_to_asm(ident);
220 let b = s.as_bytes();
221 if b.len() < 2 {
222 return false;
223 }
224 if b[0] != b'r' && b[0] != b'w' {
225 return false;
226 }
227 b[1..].iter().all(|c| c.is_ascii_digit())
228}
229
230fn is_named_operand_start(tokens: &[TokenTree], start_idx: usize) -> bool {
231 if start_idx + 1 >= tokens.len() {
232 return false;
233 }
234
235 if let TokenTree::Ident(ident) = &tokens[start_idx] {
236 if is_bpf_plain_reg_ident(ident) {
237 return false;
238 }
239 if let TokenTree::Punct(punct) = &tokens[start_idx + 1] {
240 return punct.as_char() == '=';
241 }
242 }
243 false
244}
245
246fn split_asm_and_operands(tokens: &[TokenTree]) -> (Vec<&TokenTree>, Vec<&TokenTree>) {
247 let mut asm_tokens = Vec::new();
248 let mut operand_tokens = Vec::new();
249 let mut in_operands = false;
250 let mut prev_line_num: Option<usize> = None;
251 let mut line_start_idx: Option<usize> = None;
252
253 for (i, token) in tokens.iter().enumerate() {
254 if in_operands {
255 operand_tokens.push(token);
256 continue;
257 }
258
259 let line_num = token.span().start().line;
260 let is_new_line = prev_line_num.map_or(true, |prev| line_num != prev);
261
262 if is_new_line {
263 line_start_idx = Some(i);
264 }
265
266 let at_line_start = line_start_idx == Some(i);
267
268 if at_line_start && is_asm_operand_keyword(token) {
269 in_operands = true;
270 operand_tokens.push(token);
271 } else if at_line_start && is_named_operand_start(tokens, i) {
272 in_operands = true;
273 operand_tokens.push(token);
274 } else {
275 asm_tokens.push(token);
276 }
277
278 prev_line_num = Some(line_num);
279 }
280
281 (asm_tokens, operand_tokens)
282}
283
284fn group_tokens_by_line<'a>(tokens: &[&'a TokenTree]) -> Vec<Vec<&'a TokenTree>> {
285 if tokens.is_empty() {
286 return vec![];
287 }
288
289 let mut lines: Vec<Vec<&'a TokenTree>> = vec![];
290 let mut current_line: Vec<&'a TokenTree> = vec![];
291 let mut prev_line_num: Option<usize> = None;
292
293 for &token in tokens {
294 let span = token.span();
295 let line_num = span.start().line;
296
297 if let Some(prev) = prev_line_num {
298 if line_num != prev && !current_line.is_empty() {
299 lines.push(current_line);
300 current_line = vec![];
301 }
302 }
303
304 current_line.push(token);
305 prev_line_num = Some(line_num);
306 }
307
308 if !current_line.is_empty() {
309 lines.push(current_line);
310 }
311
312 lines
313}
314
315fn format_instruction(tokens: &[&TokenTree]) -> String {
316 let mut parts: Vec<String> = vec![];
317 let mut i = 0;
318
319 while i < tokens.len() {
320 let token = tokens[i];
321 match token {
322 TokenTree::Ident(ident) => {
323 parts.push(ident_to_asm(ident));
324 }
325 TokenTree::Literal(lit) => {
326 parts.push(lit.to_string());
327 }
328 TokenTree::Punct(punct) => {
329 let ch = punct.as_char();
330 match ch {
331 ',' => {
332 parts.push(",".to_string());
333 }
334 '+' | '-' => {
335 if i + 1 < tokens.len() {
336 if let TokenTree::Literal(next_lit) = tokens[i + 1] {
337 parts.push(format!("{}{}", ch, next_lit));
338 i += 1;
339 } else {
340 parts.push(ch.to_string());
341 }
342 } else {
343 parts.push(ch.to_string());
344 }
345 }
346 '%' => {
347 if i + 1 < tokens.len() {
348 if let TokenTree::Punct(next) = tokens[i + 1] {
349 if next.as_char() == '=' {
350 parts.push("%=".to_string());
351 i += 1;
352 } else {
353 parts.push('%'.to_string());
354 }
355 } else {
356 parts.push('%'.to_string());
357 }
358 } else {
359 parts.push('%'.to_string());
360 }
361 }
362 '.' | '@' => {
365 if i + 1 < tokens.len() {
366 if let TokenTree::Ident(next_ident) = tokens[i + 1] {
367 parts.push(format!("{}{}", ch, ident_to_asm(next_ident)));
368 i += 1;
369 } else {
370 parts.push(ch.to_string());
371 }
372 } else {
373 parts.push(ch.to_string());
374 }
375 }
376 _ => {
377 parts.push(ch.to_string());
378 }
379 }
380 }
381 TokenTree::Group(group) => {
382 let delimiter = group.delimiter();
383 let inner: Vec<TokenTree> = group.stream().into_iter().collect();
384 let inner_refs: Vec<&TokenTree> = inner.iter().collect();
385 let inner_str = format_memory_operand(&inner_refs);
386
387 match delimiter {
388 proc_macro2::Delimiter::Bracket => {
389 parts.push(format!("[{}]", inner_str));
390 }
391 proc_macro2::Delimiter::Brace => {
392 parts.push(format!("{{{}}}", inner_str));
393 }
394 proc_macro2::Delimiter::Parenthesis => {
395 parts.push(format!("({})", inner_str));
396 }
397 proc_macro2::Delimiter::None => {
398 parts.push(inner_str);
399 }
400 }
401 }
402 }
403 i += 1;
404 }
405
406 join_asm_parts(&parts)
407}
408
409fn format_memory_operand(tokens: &[&TokenTree]) -> String {
410 let mut result = String::new();
411 let mut i = 0;
412
413 while i < tokens.len() {
414 let token = tokens[i];
415 match token {
416 TokenTree::Ident(ident) => {
417 result.push_str(&ident_to_asm(ident));
418 }
419 TokenTree::Literal(lit) => {
420 result.push_str(&lit.to_string());
421 }
422 TokenTree::Punct(punct) => {
423 let ch = punct.as_char();
424 if ch == '+' || ch == '-' {
425 if i + 1 < tokens.len() {
426 if let TokenTree::Literal(next_lit) = tokens[i + 1] {
427 result.push(ch);
428 result.push_str(&next_lit.to_string());
429 i += 1;
430 } else if let TokenTree::Ident(next_ident) = tokens[i + 1] {
431 result.push(ch);
432 result.push_str(&next_ident.to_string());
433 i += 1;
434 } else {
435 result.push(ch);
436 }
437 } else {
438 result.push(ch);
439 }
440 } else {
441 result.push(ch);
442 }
443 }
444 TokenTree::Group(group) => {
445 let inner: Vec<TokenTree> = group.stream().into_iter().collect();
446 let inner_refs: Vec<&TokenTree> = inner.iter().collect();
447 result.push_str(&format_memory_operand(&inner_refs));
448 }
449 }
450 i += 1;
451 }
452
453 result
454}
455
456fn ident_to_asm(ident: &proc_macro2::Ident) -> String {
457 let s = ident.to_string();
458 s.strip_prefix("r#").map(str::to_string).unwrap_or(s)
459}
460
461fn join_asm_parts(parts: &[String]) -> String {
462 if parts.is_empty() {
463 return String::new();
464 }
465
466 let mut result = String::new();
467 let mut prev_needs_space = false;
468
469 for part in parts {
470 if part == "," {
471 result.push_str(", ");
472 prev_needs_space = false;
473 } else if part.starts_with('[') || part.starts_with('{') || part.starts_with('(') {
474 if prev_needs_space {
475 result.push(' ');
476 }
477 result.push_str(part);
478 prev_needs_space = false;
479 } else if part.starts_with('+') || part.starts_with('-') {
480 if prev_needs_space {
481 result.push(' ');
482 }
483 result.push_str(part);
484 prev_needs_space = true;
485 } else if part == ":" {
486 result.push(':');
489 prev_needs_space = true;
490 } else {
491 if prev_needs_space {
492 result.push(' ');
493 }
494 result.push_str(part);
495 prev_needs_space = true;
496 }
497 }
498
499 result
500}
501
502#[cfg(test)]
503mod tests {
504 use super::*;
505 use proc_macro2::TokenStream;
506 use quote::quote;
507
508 fn test_format(input: TokenStream) -> Vec<String> {
509 let tokens: Vec<TokenTree> = input.into_iter().collect();
510 let (asm_tokens, _) = split_asm_and_operands(&tokens);
511 let lines = group_tokens_by_line(&asm_tokens);
512 lines
513 .into_iter()
514 .map(|line_tokens| format_instruction(&line_tokens))
515 .filter(|s| !s.is_empty())
516 .collect()
517 }
518
519 #[test]
520 fn test_simple_exit() {
521 let result = test_format(quote! { exit });
522 assert_eq!(result, vec!["exit"]);
523 }
524
525 #[test]
526 fn test_mov_reg_imm() {
527 let result = test_format(quote! { mov r0, 0 });
528 assert_eq!(result, vec!["mov r0, 0"]);
529 }
530
531 #[test]
532 fn test_add_reg_reg() {
533 let result = test_format(quote! { add r1, r2 });
534 assert_eq!(result, vec!["add r1, r2"]);
535 }
536
537 #[test]
538 fn test_memory_load() {
539 let result = test_format(quote! { ldxb r2, [r1 + 12] });
540 assert_eq!(result, vec!["ldxb r2, [r1+12]"]);
541 }
542
543 #[test]
544 fn test_memory_negative_offset() {
545 let result = test_format(quote! { ldxh r4, [r1 - 8] });
546 assert_eq!(result, vec!["ldxh r4, [r1-8]"]);
547 }
548
549 #[test]
550 fn test_jump_offset() {
551 let result = test_format(quote! { ja +5 });
552 assert_eq!(result, vec!["ja +5"]);
553 }
554
555 #[test]
556 fn test_jump_negative() {
557 let result = test_format(quote! { ja -3 });
558 assert_eq!(result, vec!["ja -3"]);
559 }
560
561 #[test]
562 fn test_conditional_jump() {
563 let result = test_format(quote! { jne r3, 0x8, +37 });
564 assert_eq!(result, vec!["jne r3, 0x8, +37"]);
565 }
566
567 #[test]
568 fn test_hex_immediate() {
569 let result = test_format(quote! { mov r0, 0x1234 });
570 assert_eq!(result, vec!["mov r0, 0x1234"]);
571 }
572
573 #[test]
574 fn test_label_definition_no_space_before_colon() {
575 use proc_macro2::TokenStream;
576
577 let lines: &[TokenStream] = &[
578 quote!(call cal_a),
579 quote!(ja past_cal_a),
580 quote!(cal_a:),
581 quote!(mov64 r0, 0),
582 quote!(exit),
583 quote!(past_cal_a:),
584 quote!(mov64 r1, 1),
585 ];
586 let result: Vec<String> = lines
587 .iter()
588 .flat_map(|ts| test_format(ts.clone()))
589 .collect();
590
591 assert_eq!(
592 result,
593 vec![
594 "call cal_a",
595 "ja past_cal_a",
596 "cal_a:",
597 "mov64 r0, 0",
598 "exit",
599 "past_cal_a:",
600 "mov64 r1, 1",
601 ]
602 );
603 }
604
605 #[test]
606 fn test_label_and_insn_same_line_has_space_after_colon() {
607 let result = test_format(quote!(cal_a: mov64 r0, 0));
608 assert_eq!(result, vec!["cal_a: mov64 r0, 0"]);
609 }
610
611 #[test]
612 fn test_directives_glue_dot_and_at_to_following_ident() {
613 use proc_macro2::TokenStream;
614 let lines: &[TokenStream] = &[
615 quote!(.globl entrypoint),
616 quote!(.type entrypoint, @function),
617 quote!(.section .text),
618 quote!(entrypoint:),
619 quote!(ldxdw r1, [r2 - 8]),
620 quote!(call process),
621 quote!(exit),
622 ];
623 let out: Vec<String> = lines
624 .iter()
625 .flat_map(|ts| test_format(ts.clone()))
626 .collect();
627 assert_eq!(
628 out,
629 vec![
630 ".globl entrypoint",
631 ".type entrypoint, @function",
632 ".section .text",
633 "entrypoint:",
634 "ldxdw r1, [r2-8]",
635 "call process",
636 "exit",
637 ]
638 );
639 }
640
641 #[test]
645 fn test_sbpf_make_instruction_map_mnemonics() {
646 use proc_macro2::TokenStream;
647
648 let lines: &[TokenStream] = &[
651 quote!(lddw r1, 0x1122334455667788_u64),
652 quote!(ja +1),
653 quote!(syscall sol_log_),
654 quote!(call callee),
655 quote!(callx r5),
656 quote!(exit),
657 quote!(neg r3),
658 quote!(neg32 r3),
659 quote!(neg64 r3),
660 quote!(add r3, r4),
661 quote!(add32 r3, r4),
662 quote!(add64 r3, r4),
663 quote!(sub r3, r4),
664 quote!(sub32 r3, r4),
665 quote!(sub64 r3, r4),
666 quote!(mul r3, r4),
667 quote!(mul32 r3, r4),
668 quote!(mul64 r3, r4),
669 quote!(div r3, r4),
670 quote!(div32 r3, r4),
671 quote!(div64 r3, r4),
672 quote!(or r3, r4),
673 quote!(or32 r3, r4),
674 quote!(or64 r3, r4),
675 quote!(and r3, r4),
676 quote!(and32 r3, r4),
677 quote!(and64 r3, r4),
678 quote!(lsh r3, r4),
679 quote!(lsh32 r3, r4),
680 quote!(lsh64 r3, r4),
681 quote!(rsh r3, r4),
682 quote!(rsh32 r3, r4),
683 quote!(rsh64 r3, r4),
684 quote!(r#mod r3, r4),
685 quote!(mod32 r3, r4),
686 quote!(mod64 r3, r4),
687 quote!(xor r3, r4),
688 quote!(xor32 r3, r4),
689 quote!(xor64 r3, r4),
690 quote!(mov r3, r4),
691 quote!(mov32 r3, r4),
692 quote!(mov64 r3, r4),
693 quote!(arsh r3, r4),
694 quote!(arsh32 r3, r4),
695 quote!(arsh64 r3, r4),
696 quote!(hor r3, r4),
697 quote!(hor32 r3, r4),
698 quote!(hor64 r3, r4),
699 quote!(lmul r3, r4),
700 quote!(lmul32 r3, r4),
701 quote!(lmul64 r3, r4),
702 quote!(uhmul r3, r4),
703 quote!(uhmul64 r3, r4),
704 quote!(shmul r3, r4),
705 quote!(shmul64 r3, r4),
706 quote!(udiv r3, r4),
707 quote!(udiv32 r3, r4),
708 quote!(udiv64 r3, r4),
709 quote!(urem r3, r4),
710 quote!(urem32 r3, r4),
711 quote!(urem64 r3, r4),
712 quote!(sdiv r3, r4),
713 quote!(sdiv32 r3, r4),
714 quote!(sdiv64 r3, r4),
715 quote!(srem r3, r4),
716 quote!(srem32 r3, r4),
717 quote!(srem64 r3, r4),
718 quote!(ldxb r2, [r1 + 0]),
719 quote!(ldxh r2, [r1 + 0]),
720 quote!(ldxw r2, [r1 + 0]),
721 quote!(ldxdw r2, [r1 + 0]),
722 quote!(stb [r10 - 8], 1),
723 quote!(sth [r10 - 8], 1),
724 quote!(stw [r10 - 8], 1),
725 quote!(stdw [r10 - 8], 1),
726 quote!(stxb [r10 - 8], r1),
727 quote!(stxh [r10 - 8], r1),
728 quote!(stxw [r10 - 8], r1),
729 quote!(stxdw [r10 - 8], r1),
730 quote!(jeq r1, r2, +1),
731 quote!(jeq32 r1, r2, +1),
732 quote!(jeq64 r1, r2, +1),
733 quote!(jgt r1, r2, +1),
734 quote!(jgt32 r1, r2, +1),
735 quote!(jgt64 r1, r2, +1),
736 quote!(jge r1, r2, +1),
737 quote!(jge32 r1, r2, +1),
738 quote!(jge64 r1, r2, +1),
739 quote!(jlt r1, r2, +1),
740 quote!(jlt32 r1, r2, +1),
741 quote!(jlt64 r1, r2, +1),
742 quote!(jle r1, r2, +1),
743 quote!(jle32 r1, r2, +1),
744 quote!(jle64 r1, r2, +1),
745 quote!(jset r1, r2, +1),
746 quote!(jset32 r1, r2, +1),
747 quote!(jset64 r1, r2, +1),
748 quote!(jne r1, r2, +1),
749 quote!(jne32 r1, r2, +1),
750 quote!(jne64 r1, r2, +1),
751 quote!(jsgt r1, r2, +1),
752 quote!(jsgt32 r1, r2, +1),
753 quote!(jsgt64 r1, r2, +1),
754 quote!(jsge r1, r2, +1),
755 quote!(jsge32 r1, r2, +1),
756 quote!(jsge64 r1, r2, +1),
757 quote!(jslt r1, r2, +1),
758 quote!(jslt32 r1, r2, +1),
759 quote!(jslt64 r1, r2, +1),
760 quote!(jsle r1, r2, +1),
761 quote!(jsle32 r1, r2, +1),
762 quote!(jsle64 r1, r2, +1),
763 quote!(be16 r2),
764 quote!(be32 r2),
765 quote!(be64 r2),
766 quote!(le16 r2),
767 quote!(le32 r2),
768 quote!(le64 r2),
769 ];
770
771 let out: Vec<String> = lines
772 .iter()
773 .flat_map(|ts| test_format(ts.clone()))
774 .collect();
775
776 assert_eq!(
777 out,
778 vec![
779 "lddw r1, 0x1122334455667788_u64",
780 "ja +1",
781 "syscall sol_log_",
782 "call callee",
783 "callx r5",
784 "exit",
785 "neg r3",
786 "neg32 r3",
787 "neg64 r3",
788 "add r3, r4",
789 "add32 r3, r4",
790 "add64 r3, r4",
791 "sub r3, r4",
792 "sub32 r3, r4",
793 "sub64 r3, r4",
794 "mul r3, r4",
795 "mul32 r3, r4",
796 "mul64 r3, r4",
797 "div r3, r4",
798 "div32 r3, r4",
799 "div64 r3, r4",
800 "or r3, r4",
801 "or32 r3, r4",
802 "or64 r3, r4",
803 "and r3, r4",
804 "and32 r3, r4",
805 "and64 r3, r4",
806 "lsh r3, r4",
807 "lsh32 r3, r4",
808 "lsh64 r3, r4",
809 "rsh r3, r4",
810 "rsh32 r3, r4",
811 "rsh64 r3, r4",
812 "mod r3, r4",
813 "mod32 r3, r4",
814 "mod64 r3, r4",
815 "xor r3, r4",
816 "xor32 r3, r4",
817 "xor64 r3, r4",
818 "mov r3, r4",
819 "mov32 r3, r4",
820 "mov64 r3, r4",
821 "arsh r3, r4",
822 "arsh32 r3, r4",
823 "arsh64 r3, r4",
824 "hor r3, r4",
825 "hor32 r3, r4",
826 "hor64 r3, r4",
827 "lmul r3, r4",
828 "lmul32 r3, r4",
829 "lmul64 r3, r4",
830 "uhmul r3, r4",
831 "uhmul64 r3, r4",
832 "shmul r3, r4",
833 "shmul64 r3, r4",
834 "udiv r3, r4",
835 "udiv32 r3, r4",
836 "udiv64 r3, r4",
837 "urem r3, r4",
838 "urem32 r3, r4",
839 "urem64 r3, r4",
840 "sdiv r3, r4",
841 "sdiv32 r3, r4",
842 "sdiv64 r3, r4",
843 "srem r3, r4",
844 "srem32 r3, r4",
845 "srem64 r3, r4",
846 "ldxb r2, [r1+0]",
847 "ldxh r2, [r1+0]",
848 "ldxw r2, [r1+0]",
849 "ldxdw r2, [r1+0]",
850 "stb [r10-8], 1",
851 "sth [r10-8], 1",
852 "stw [r10-8], 1",
853 "stdw [r10-8], 1",
854 "stxb [r10-8], r1",
855 "stxh [r10-8], r1",
856 "stxw [r10-8], r1",
857 "stxdw [r10-8], r1",
858 "jeq r1, r2, +1",
859 "jeq32 r1, r2, +1",
860 "jeq64 r1, r2, +1",
861 "jgt r1, r2, +1",
862 "jgt32 r1, r2, +1",
863 "jgt64 r1, r2, +1",
864 "jge r1, r2, +1",
865 "jge32 r1, r2, +1",
866 "jge64 r1, r2, +1",
867 "jlt r1, r2, +1",
868 "jlt32 r1, r2, +1",
869 "jlt64 r1, r2, +1",
870 "jle r1, r2, +1",
871 "jle32 r1, r2, +1",
872 "jle64 r1, r2, +1",
873 "jset r1, r2, +1",
874 "jset32 r1, r2, +1",
875 "jset64 r1, r2, +1",
876 "jne r1, r2, +1",
877 "jne32 r1, r2, +1",
878 "jne64 r1, r2, +1",
879 "jsgt r1, r2, +1",
880 "jsgt32 r1, r2, +1",
881 "jsgt64 r1, r2, +1",
882 "jsge r1, r2, +1",
883 "jsge32 r1, r2, +1",
884 "jsge64 r1, r2, +1",
885 "jslt r1, r2, +1",
886 "jslt32 r1, r2, +1",
887 "jslt64 r1, r2, +1",
888 "jsle r1, r2, +1",
889 "jsle32 r1, r2, +1",
890 "jsle64 r1, r2, +1",
891 "be16 r2",
892 "be32 r2",
893 "be64 r2",
894 "le16 r2",
895 "le32 r2",
896 "le64 r2",
897 ]
898 );
899 }
900}