2 * Copyright (C) 1995-2008 University of Karlsruhe. All right reserved.
4 * This file is part of libFirm.
6 * This file may be distributed and/or modified under the terms of the
7 * GNU General Public License version 2 as published by the Free Software
8 * Foundation and appearing in the file LICENSE.GPL included in the
9 * packaging of this file.
11 * Licensees holding valid libFirm Professional Edition licenses may use
12 * this file in accordance with the libFirm Commercial License.
13 * Agreement provided with the Software.
15 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
16 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * @author Oliver Richter, Tobias Gneist, Michael Beck
41 #include "raw_bitset.h"
45 #include "beblocksched.h"
50 #include "arm_emitter.h"
51 #include "arm_optimize.h"
52 #include "gen_arm_emitter.h"
53 #include "arm_nodes_attr.h"
54 #include "arm_new_nodes.h"
55 #include "arm_map_regs.h"
56 #include "gen_arm_regalloc_if.h"
60 DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;)
62 static set *sym_or_tv;
63 static arm_isa_t *isa;
65 static void arm_emit_register(const arch_register_t *reg)
67 be_emit_string(arch_register_get_name(reg));
70 static void arm_emit_source_register(const ir_node *node, int pos)
72 const arch_register_t *reg = arch_get_irn_register_in(node, pos);
73 arm_emit_register(reg);
76 static void arm_emit_dest_register(const ir_node *node, int pos)
78 const arch_register_t *reg = arch_get_irn_register_out(node, pos);
79 arm_emit_register(reg);
82 static void arm_emit_offset(const ir_node *node)
84 const arm_load_store_attr_t *attr = get_arm_load_store_attr_const(node);
85 assert(attr->base.is_load_store);
87 be_emit_irprintf("0x%X", attr->offset);
91 * Emit the arm fpa instruction suffix depending on the mode.
93 static void arm_emit_fpa_postfix(const ir_mode *mode)
95 int bits = get_mode_size_bits(mode);
105 static void arm_emit_float_load_store_mode(const ir_node *node)
107 const arm_load_store_attr_t *attr = get_arm_load_store_attr_const(node);
108 arm_emit_fpa_postfix(attr->load_store_mode);
111 static void arm_emit_float_arithmetic_mode(const ir_node *node)
113 const arm_farith_attr_t *attr = get_arm_farith_attr_const(node);
114 arm_emit_fpa_postfix(attr->mode);
117 static void arm_emit_symconst(const ir_node *node)
119 const arm_SymConst_attr_t *symconst = get_arm_SymConst_attr_const(node);
120 ir_entity *entity = symconst->entity;
122 be_gas_emit_entity(entity);
124 /* TODO do something with offset */
127 static void arm_emit_load_mode(const ir_node *node)
129 const arm_load_store_attr_t *attr = get_arm_load_store_attr_const(node);
130 ir_mode *mode = attr->load_store_mode;
131 int bits = get_mode_size_bits(mode);
132 bool is_signed = mode_is_signed(mode);
134 be_emit_string(is_signed ? "sh" : "h");
135 } else if (bits == 8) {
136 be_emit_string(is_signed ? "sb" : "b");
142 static void arm_emit_store_mode(const ir_node *node)
144 const arm_load_store_attr_t *attr = get_arm_load_store_attr_const(node);
145 ir_mode *mode = attr->load_store_mode;
146 int bits = get_mode_size_bits(mode);
148 be_emit_cstring("h");
149 } else if (bits == 8) {
150 be_emit_cstring("b");
156 static void emit_shf_mod_name(arm_shift_modifier_t mod)
159 case ARM_SHF_ASR_REG:
160 case ARM_SHF_ASR_IMM:
161 be_emit_cstring("asr");
163 case ARM_SHF_LSL_REG:
164 case ARM_SHF_LSL_IMM:
165 be_emit_cstring("lsl");
167 case ARM_SHF_LSR_REG:
168 case ARM_SHF_LSR_IMM:
169 be_emit_cstring("lsr");
171 case ARM_SHF_ROR_REG:
172 case ARM_SHF_ROR_IMM:
173 be_emit_cstring("ror");
178 panic("can't emit this shf_mod_name %d", (int) mod);
181 static void arm_emit_shifter_operand(const ir_node *node)
183 const arm_shifter_operand_t *attr = get_arm_shifter_operand_attr_const(node);
185 switch (attr->shift_modifier) {
187 arm_emit_source_register(node, get_irn_arity(node) - 1);
190 unsigned val = attr->immediate_value;
191 val = (val >> attr->shift_immediate)
192 | (val << (32-attr->shift_immediate));
194 be_emit_irprintf("#0x%X", val);
197 case ARM_SHF_ASR_IMM:
198 case ARM_SHF_LSL_IMM:
199 case ARM_SHF_LSR_IMM:
200 case ARM_SHF_ROR_IMM:
201 arm_emit_source_register(node, get_irn_arity(node) - 1);
202 be_emit_cstring(", ");
203 emit_shf_mod_name(attr->shift_modifier);
204 be_emit_irprintf(" #0x%X", attr->shift_immediate);
207 case ARM_SHF_ASR_REG:
208 case ARM_SHF_LSL_REG:
209 case ARM_SHF_LSR_REG:
210 case ARM_SHF_ROR_REG:
211 arm_emit_source_register(node, get_irn_arity(node) - 2);
212 be_emit_cstring(", ");
213 emit_shf_mod_name(attr->shift_modifier);
214 be_emit_cstring(" ");
215 arm_emit_source_register(node, get_irn_arity(node) - 1);
219 arm_emit_source_register(node, get_irn_arity(node) - 1);
220 panic("RRX shifter emitter TODO");
222 case ARM_SHF_INVALID:
225 panic("Invalid shift_modifier while emitting %+F", node);
228 /** An entry in the sym_or_tv set. */
229 typedef struct sym_or_tv_t {
231 ir_entity *entity; /**< An entity. */
232 ir_tarval *tv; /**< A tarval. */
233 const void *generic; /**< For generic compare. */
235 unsigned label; /**< the associated label. */
236 bool is_entity; /**< true if an entity is stored. */
240 * Returns a unique label. This number will not be used a second time.
242 static unsigned get_unique_label(void)
244 static unsigned id = 0;
248 static void emit_constant_name(const sym_or_tv_t *entry)
250 be_emit_irprintf("%sC%u", be_gas_get_private_prefix(), entry->label);
254 * Returns the target block for a control flow node.
256 static ir_node *get_cfop_target_block(const ir_node *irn)
258 return (ir_node*)get_irn_link(irn);
262 * Emit the target label for a control flow node.
264 static void arm_emit_cfop_target(const ir_node *irn)
266 ir_node *block = get_cfop_target_block(irn);
268 be_gas_emit_block_name(block);
271 void arm_emitf(const ir_node *node, const char *format, ...)
274 va_start(ap, format);
277 const char *start = format;
278 while (*format != '%' && *format != '\0')
280 be_emit_string_len(start, format-start);
291 if (*format < '0' || '9' <= *format)
293 unsigned const pos = *format++ - '0';
294 arm_emit_source_register(node, pos);
299 if (*format < '0' || '9' <= *format)
301 unsigned const pos = *format++ - '0';
302 arm_emit_dest_register(node, pos);
307 arm_emit_symconst(node);
311 arm_emit_offset(node);
315 arm_emit_shifter_operand(node);
319 const sym_or_tv_t *name = va_arg(ap, const sym_or_tv_t*);
320 emit_constant_name(name);
325 ir_mode *mode = va_arg(ap, ir_mode*);
326 arm_emit_fpa_postfix(mode);
332 case 'L': arm_emit_load_mode(node); break;
333 case 'S': arm_emit_store_mode(node); break;
334 case 'A': arm_emit_float_arithmetic_mode(node); break;
335 case 'F': arm_emit_float_load_store_mode(node); break;
343 int num = va_arg(ap, int);
344 be_emit_irprintf("%X", num);
349 unsigned num = va_arg(ap, unsigned);
350 be_emit_irprintf("%u", num);
355 int num = va_arg(ap, int);
356 be_emit_irprintf("%d", num);
361 const char *string = va_arg(ap, const char *);
362 be_emit_string(string);
367 arch_register_t *reg = va_arg(ap, arch_register_t*);
368 arm_emit_register(reg);
373 const ir_node *n = va_arg(ap, const ir_node*);
374 arm_emit_cfop_target(n);
380 be_emit_write_line();
386 panic("unknown format conversion in arm_emitf()");
390 be_emit_finish_line_gas(node);
396 static void emit_arm_SymConst(const ir_node *irn)
398 const arm_SymConst_attr_t *attr = get_arm_SymConst_attr_const(irn);
399 sym_or_tv_t key, *entry;
401 key.u.entity = attr->entity;
402 key.is_entity = true;
404 entry = set_insert(sym_or_tv_t, sym_or_tv, &key, sizeof(key), hash_ptr(key.u.generic));
405 if (entry->label == 0) {
406 /* allocate a label */
407 entry->label = get_unique_label();
410 /* load the symbol indirect */
411 arm_emitf(irn, "ldr %D0, %C", entry);
414 static void emit_arm_FrameAddr(const ir_node *irn)
416 const arm_SymConst_attr_t *attr = get_arm_SymConst_attr_const(irn);
417 arm_emitf(irn, "add %D0, %S0, #0x%X", attr->fp_offset);
421 * Emit a floating point fpa constant.
423 static void emit_arm_fConst(const ir_node *irn)
427 key.u.tv = get_fConst_value(irn);
428 key.is_entity = false;
430 sym_or_tv_t *entry = set_insert(sym_or_tv_t, sym_or_tv, &key, sizeof(key), hash_ptr(key.u.generic));
431 if (entry->label == 0) {
432 /* allocate a label */
433 entry->label = get_unique_label();
436 /* load the tarval indirect */
437 ir_mode *mode = get_irn_mode(irn);
438 arm_emitf(irn, "ldf%m %D0, %C", mode, entry);
442 * Returns the next block in a block schedule.
444 static ir_node *sched_next_block(const ir_node *block)
446 return (ir_node*)get_irn_link(block);
450 * Emit a Compare with conditional branch.
452 static void emit_arm_B(const ir_node *irn)
454 const ir_node *proj_true = NULL;
455 const ir_node *proj_false = NULL;
456 const ir_node *block;
457 const ir_node *next_block;
458 ir_node *op1 = get_irn_n(irn, 0);
460 ir_relation relation = get_arm_CondJmp_relation(irn);
461 const arm_cmp_attr_t *cmp_attr = get_arm_cmp_attr_const(op1);
462 bool is_signed = !cmp_attr->is_unsigned;
464 assert(is_arm_Cmp(op1) || is_arm_Tst(op1));
466 foreach_out_edge(irn, edge) {
467 ir_node *proj = get_edge_src_irn(edge);
468 long nr = get_Proj_proj(proj);
469 if (nr == pn_Cond_true) {
476 if (cmp_attr->ins_permuted) {
477 relation = get_inversed_relation(relation);
480 /* for now, the code works for scheduled and non-schedules blocks */
481 block = get_nodes_block(irn);
483 /* we have a block schedule */
484 next_block = sched_next_block(block);
486 assert(relation != ir_relation_false);
487 assert(relation != ir_relation_true);
489 if (get_cfop_target_block(proj_true) == next_block) {
490 /* exchange both proj's so the second one can be omitted */
491 const ir_node *t = proj_true;
493 proj_true = proj_false;
495 relation = get_negated_relation(relation);
498 switch (relation & (ir_relation_less_equal_greater)) {
499 case ir_relation_equal: suffix = "eq"; break;
500 case ir_relation_less: suffix = is_signed ? "lt" : "lo"; break;
501 case ir_relation_less_equal: suffix = is_signed ? "le" : "ls"; break;
502 case ir_relation_greater: suffix = is_signed ? "gt" : "hi"; break;
503 case ir_relation_greater_equal: suffix = is_signed ? "ge" : "hs"; break;
504 case ir_relation_less_greater: suffix = "ne"; break;
505 case ir_relation_less_equal_greater: suffix = "al"; break;
506 default: panic("Cmp has unsupported relation");
509 /* emit the true proj */
510 arm_emitf(irn, "b%s %t", suffix, proj_true);
512 if (get_cfop_target_block(proj_false) == next_block) {
513 if (be_options.verbose_asm) {
514 arm_emitf(irn, "/* fallthrough to %t */", proj_false);
517 arm_emitf(irn, "b %t", proj_false);
521 /** Sort register in ascending order. */
522 static int reg_cmp(const void *a, const void *b)
524 const arch_register_t * const *ra = (const arch_register_t**)a;
525 const arch_register_t * const *rb = (const arch_register_t**)b;
527 return *ra < *rb ? -1 : (*ra != *rb);
531 * Create the CopyB instruction sequence.
533 static void emit_arm_CopyB(const ir_node *irn)
535 const arm_CopyB_attr_t *attr = get_arm_CopyB_attr_const(irn);
536 unsigned size = attr->size;
537 const arch_register_t *tmpregs[4];
539 /* collect the temporary registers and sort them, we need ascending order */
540 tmpregs[0] = arch_get_irn_register_in(irn, 2);
541 tmpregs[1] = arch_get_irn_register_in(irn, 3);
542 tmpregs[2] = arch_get_irn_register_in(irn, 4);
543 tmpregs[3] = &arm_registers[REG_R12];
545 /* Note: R12 is always the last register because the RA did not assign higher ones */
546 qsort((void *)tmpregs, 3, sizeof(tmpregs[0]), reg_cmp);
548 if (be_options.verbose_asm) {
549 arm_emitf(irn, "/* MemCopy (%S1)->(%S0) [%u bytes], Uses %r, %r, %r and %r */",
550 size, tmpregs[0], tmpregs[1], tmpregs[2], tmpregs[3]);
553 assert(size > 0 && "CopyB needs size > 0" );
556 fprintf(stderr, "strange hack enabled: copy more bytes than needed!");
565 arm_emitf(irn, "ldr %r, [%S1, #0]", tmpregs[3]);
566 arm_emitf(irn, "str %r, [%S0, #0]", tmpregs[3]);
569 arm_emitf(irn, "ldmia %S1!, {%r, %r}", tmpregs[0], tmpregs[1]);
570 arm_emitf(irn, "stmia %S0!, {%r, %r}", tmpregs[0], tmpregs[1]);
573 arm_emitf(irn, "ldmia %S1!, {%r, %r, %r}", tmpregs[0], tmpregs[1], tmpregs[2]);
574 arm_emitf(irn, "stmia %S0!, {%r, %r, %r}", tmpregs[0], tmpregs[1], tmpregs[2]);
579 arm_emitf(irn, "ldmia %S1!, {%r, %r, %r}", tmpregs[0], tmpregs[1], tmpregs[2], tmpregs[3]);
580 arm_emitf(irn, "stmia %S0!, {%r, %r, %r}", tmpregs[0], tmpregs[1], tmpregs[2], tmpregs[3]);
585 static void emit_arm_SwitchJmp(const ir_node *irn)
587 const arm_SwitchJmp_attr_t *attr = get_arm_SwitchJmp_attr_const(irn);
588 arm_emitf(irn, "ldrls pc, [pc, %S0, asl #2]");
590 be_emit_jump_table(irn, attr->table, NULL, get_cfop_target_block);
593 /** Emit an IncSP node */
594 static void emit_be_IncSP(const ir_node *irn)
596 int offs = -be_get_IncSP_offset(irn);
601 const char *op = "add";
606 arm_emitf(irn, "%s %D0, %S0, #0x%X", op, offs);
609 static void emit_be_Copy(const ir_node *irn)
611 ir_mode *mode = get_irn_mode(irn);
613 if (arch_get_irn_register_in(irn, 0) == arch_get_irn_register_out(irn, 0)) {
618 if (mode_is_float(mode)) {
620 arm_emitf(irn, "mvf %D0, %S0");
622 panic("emit_be_Copy: move not supported for this mode");
624 } else if (mode_is_data(mode)) {
625 arm_emitf(irn, "mov %D0, %S0");
627 panic("emit_be_Copy: move not supported for this mode");
631 static void emit_be_Perm(const ir_node *irn)
634 "eor %S0, %S0, %S1\n"
635 "eor %S1, %S0, %S1\n"
636 "eor %S0, %S0, %S1");
639 static void emit_be_MemPerm(const ir_node *node)
645 /* TODO: this implementation is slower than necessary.
646 The longterm goal is however to avoid the memperm node completely */
648 memperm_arity = be_get_MemPerm_entity_arity(node);
649 if (memperm_arity > 12)
650 panic("memperm with more than 12 inputs not supported yet");
652 for (i = 0; i < memperm_arity; ++i) {
654 arm_emitf(node, "str r%d, [sp, #-4]!", i);
656 /* load from entity */
657 ir_entity *entity = be_get_MemPerm_in_entity(node, i);
658 int offset = get_entity_offset(entity) + sp_change;
659 arm_emitf(node, "ldr r%d, [sp, #%d]", i, offset);
662 for (i = memperm_arity-1; i >= 0; --i) {
663 /* store to new entity */
664 ir_entity *entity = be_get_MemPerm_out_entity(node, i);
665 int offset = get_entity_offset(entity) + sp_change;
666 arm_emitf(node, "str r%d, [sp, #%d]", i, offset);
667 /* restore register */
668 arm_emitf(node, "ldr r%d, [sp], #4", i);
671 assert(sp_change == 0);
674 static void emit_be_Start(const ir_node *node)
676 ir_graph *irg = get_irn_irg(node);
677 ir_type *frame_type = get_irg_frame_type(irg);
678 unsigned size = get_type_size_bytes(frame_type);
680 /* allocate stackframe */
682 arm_emitf(node, "sub sp, sp, #0x%X", size);
686 static void emit_be_Return(const ir_node *node)
688 ir_graph *irg = get_irn_irg(node);
689 ir_type *frame_type = get_irg_frame_type(irg);
690 unsigned size = get_type_size_bytes(frame_type);
692 /* deallocate stackframe */
694 arm_emitf(node, "add sp, sp, #0x%X", size);
696 arm_emitf(node, "mov pc, lr");
700 static void emit_arm_Jmp(const ir_node *node)
702 ir_node *block, *next_block;
704 /* for now, the code works for scheduled and non-schedules blocks */
705 block = get_nodes_block(node);
707 /* we have a block schedule */
708 next_block = sched_next_block(block);
709 if (get_cfop_target_block(node) != next_block) {
710 arm_emitf(node, "b %t", node);
712 if (be_options.verbose_asm) {
713 arm_emitf(node, "/* fallthrough to %t */", node);
718 static void emit_nothing(const ir_node *irn)
724 * The type of a emitter function.
726 typedef void (emit_func)(const ir_node *irn);
729 * Set a node emitter. Make it a bit more type safe.
731 static inline void set_emitter(ir_op *op, emit_func arm_emit_node)
733 op->ops.generic = (op_func)arm_emit_node;
737 * Enters the emitter functions for handled nodes into the generic
738 * pointer of an opcode.
740 static void arm_register_emitters(void)
742 /* first clear the generic function pointer for all ops */
743 ir_clear_opcodes_generic_func();
745 /* register all emitter functions defined in spec */
746 arm_register_spec_emitters();
749 set_emitter(op_arm_B, emit_arm_B);
750 set_emitter(op_arm_CopyB, emit_arm_CopyB);
751 set_emitter(op_arm_fConst, emit_arm_fConst);
752 set_emitter(op_arm_FrameAddr, emit_arm_FrameAddr);
753 set_emitter(op_arm_Jmp, emit_arm_Jmp);
754 set_emitter(op_arm_SwitchJmp, emit_arm_SwitchJmp);
755 set_emitter(op_arm_SymConst, emit_arm_SymConst);
756 set_emitter(op_be_Copy, emit_be_Copy);
757 set_emitter(op_be_CopyKeep, emit_be_Copy);
758 set_emitter(op_be_IncSP, emit_be_IncSP);
759 set_emitter(op_be_MemPerm, emit_be_MemPerm);
760 set_emitter(op_be_Perm, emit_be_Perm);
761 set_emitter(op_be_Return, emit_be_Return);
762 set_emitter(op_be_Start, emit_be_Start);
764 /* no need to emit anything for the following nodes */
765 set_emitter(op_Phi, emit_nothing);
766 set_emitter(op_be_Keep, emit_nothing);
770 * Emits code for a node.
772 static void arm_emit_node(const ir_node *irn)
774 ir_op *op = get_irn_op(irn);
776 if (op->ops.generic) {
777 emit_func *emit = (emit_func *)op->ops.generic;
778 be_dwarf_location(get_irn_dbg_info(irn));
781 panic("Error: No emit handler for node %+F (graph %+F)\n",
782 irn, get_irn_irg(irn));
787 * emit the block label if needed.
789 static void arm_emit_block_header(ir_node *block, ir_node *prev)
791 bool need_label = false;
794 n_cfgpreds = get_Block_n_cfgpreds(block);
795 if (n_cfgpreds == 1) {
796 ir_node *pred = get_Block_cfgpred(block, 0);
797 ir_node *pred_block = get_nodes_block(pred);
799 /* we don't need labels for fallthrough blocks, however switch-jmps
800 * are no fallthroughs */
801 if (pred_block == prev &&
802 !(is_Proj(pred) && is_arm_SwitchJmp(get_Proj_pred(pred)))) {
811 be_gas_begin_block(block, need_label);
815 * Walks over the nodes in a block connected by scheduling edges
816 * and emits code for each node.
818 static void arm_gen_block(ir_node *block, ir_node *prev_block)
820 arm_emit_block_header(block, prev_block);
821 be_dwarf_location(get_irn_dbg_info(block));
822 sched_foreach(block, irn) {
829 * Sets labels for control flow nodes (jump target)
831 static void arm_gen_labels(ir_node *block, void *env)
834 int n = get_Block_n_cfgpreds(block);
837 for (n--; n >= 0; n--) {
838 pred = get_Block_cfgpred(block, n);
839 set_irn_link(pred, block);
844 * Compare two entries of the symbol or tarval set.
846 static int cmp_sym_or_tv(const void *elt, const void *key, size_t size)
848 const sym_or_tv_t *p1 = (const sym_or_tv_t*)elt;
849 const sym_or_tv_t *p2 = (const sym_or_tv_t*)key;
852 /* as an identifier NEVER can point to a tarval, it's enough
853 to compare it this way */
854 return p1->u.generic != p2->u.generic;
857 void arm_gen_routine(ir_graph *irg)
859 ir_node *last_block = NULL;
860 ir_entity *entity = get_irg_entity(irg);
861 const arch_env_t *arch_env = be_get_irg_arch_env(irg);
865 isa = (arm_isa_t*) arch_env;
866 sym_or_tv = new_set(cmp_sym_or_tv, 8);
868 be_gas_elf_type_char = '%';
870 arm_register_emitters();
872 /* create the block schedule */
873 blk_sched = be_create_block_schedule(irg);
875 be_gas_emit_function_prolog(entity, 4, NULL);
877 irg_block_walk_graph(irg, arm_gen_labels, NULL, NULL);
879 n = ARR_LEN(blk_sched);
880 for (i = 0; i < n;) {
881 ir_node *block, *next_bl;
883 block = blk_sched[i];
885 next_bl = i < n ? blk_sched[i] : NULL;
887 /* set here the link. the emitter expects to find the next block here */
888 set_irn_link(block, next_bl);
889 arm_gen_block(block, last_block);
893 /* emit SymConst values */
894 if (set_count(sym_or_tv) > 0) {
895 be_emit_cstring("\t.align 2\n");
897 foreach_set(sym_or_tv, sym_or_tv_t, entry) {
898 emit_constant_name(entry);
899 be_emit_cstring(":\n");
900 be_emit_write_line();
902 if (entry->is_entity) {
903 be_emit_cstring("\t.word\t");
904 be_gas_emit_entity(entry->u.entity);
906 be_emit_write_line();
908 ir_tarval *tv = entry->u.tv;
910 int size = get_mode_size_bytes(get_tarval_mode(tv));
912 /* beware: ARM fpa uses big endian format */
913 for (vi = ((size + 3) & ~3) - 4; vi >= 0; vi -= 4) {
916 v = get_tarval_sub_bits(tv, vi+3);
917 v = (v << 8) | get_tarval_sub_bits(tv, vi+2);
918 v = (v << 8) | get_tarval_sub_bits(tv, vi+1);
919 v = (v << 8) | get_tarval_sub_bits(tv, vi+0);
920 be_emit_irprintf("\t.word\t%u\n", v);
921 be_emit_write_line();
926 be_emit_write_line();
930 be_gas_emit_function_epilog(entity);
933 void arm_init_emitter(void)
935 FIRM_DBG_REGISTER(dbg, "firm.be.arm.emit");