## `__asm__` Inline Assembly Syntax
-This directive is a very limited GNU-style inline assembly extension.
-It allows hand-written assembly to be inserted directly into the
-generated assembly output.
+This directive supports GNU-style extended inline assembly, including
+output operands, input operands, read/write operands, register and
+memory constraints, `volatile`, and clobber lists.
As Intel syntax is the primary target, arguments are written
left-to-right rather than right-to-left.
char constraint[16];
char subst[64];
+ char output_name[64];
+
+ const char *reg_name;
+
+ int size;
+ int is_output;
+ int output_in_memory;
int restore_reg;
+
const char *restore_name;
};
}
+static void inline_asm_read_string_sequence (char *dst, size_t dst_size) {
+
+ char part[256];
+
+ size_t used;
+ size_t avail;
+ size_t part_len;
+
+ if (!dst || dst_size == 0) {
+ return;
+ }
+
+ dst[0] = 0;
+
+ while (is_string_token ()) {
+
+ inline_asm_unquote_string_token (part, sizeof (part));
+
+ used = strlen (dst);
+ avail = dst_size - used - 1;
+
+ if (avail) {
+
+ part_len = strlen (part);
+
+ if (part_len > avail) {
+ part_len = avail;
+ }
+
+ memcpy (dst + used, part, part_len);
+ dst[used + part_len] = 0;
+
+ }
+
+ get_token ();
+
+ }
+
+}
+
static const char *inline_asm_constraint_reg32 (const char *constraint) {
if (!constraint) {
return "rdi";
}
+ if (strchr (constraint, 'r')) {
+ return "rax";
+ }
+
return 0;
}
-static const char *inline_asm_reg_for_template (const char *reg32) {
+static const char *inline_asm_reg_for_size (const char *reg, int size) {
- if (!reg32) {
+ if (!reg) {
return "";
}
- if (strcmp (reg32, "rax") == 0) {
- return "al";
+ if (strcmp (reg, "rax") == 0) {
+
+ if (size == 1) {
+ return "al";
+ }
+
+ if (size == 2) {
+ return "ax";
+ }
+
+ if (size == 8) {
+ return "rax";
+ }
+
+ return "eax";
+
}
- if (strcmp (reg32, "rbx") == 0) {
- return "bl";
+ if (strcmp (reg, "rbx") == 0) {
+
+ if (size == 1) {
+ return "bl";
+ }
+
+ if (size == 2) {
+ return "bx";
+ }
+
+ if (size == 8) {
+ return "rbx";
+ }
+
+ return "ebx";
+
}
- if (strcmp (reg32, "rcx") == 0) {
- return "cl";
+ if (strcmp (reg, "rcx") == 0) {
+
+ if (size == 1) {
+ return "cl";
+ }
+
+ if (size == 2) {
+ return "cx";
+ }
+
+ if (size == 8) {
+ return "rcx";
+ }
+
+ return "ecx";
+
}
- if (strcmp (reg32, "rdx") == 0) {
- return "dx";
+ if (strcmp (reg, "rdx") == 0) {
+
+ if (size == 1) {
+ return "dl";
+ }
+
+ if (size == 2) {
+ return "dx";
+ }
+
+ if (size == 8) {
+ return "rdx";
+ }
+
+ return "edx";
+
}
- if (strcmp (reg32, "rsi") == 0) {
- return "rsi";
+ if (strcmp (reg, "rsi") == 0) {
+
+ if (size == 1) {
+ return "sil";
+ }
+
+ if (size == 2) {
+ return "si";
+ }
+
+ if (size == 8) {
+ return "rsi";
+ }
+
+ return "esi";
+
}
- if (strcmp (reg32, "rdi") == 0) {
- return "rdi";
+ if (strcmp (reg, "rdi") == 0) {
+
+ if (size == 1) {
+ return "dil";
+ }
+
+ if (size == 2) {
+ return "di";
+ }
+
+ if (size == 8) {
+ return "rdi";
+ }
+
+ return "edi";
+
}
- return reg32;
+ return reg;
}
}
+static int inline_asm_identifier_size (const char *name) {
+
+ struct local_symbol *sym;
+
+ if (!name || !*name) {
+ return DATA_INT & 0x1f;
+ }
+
+ sym = find_local_symbol (name);
+
+ if (sym) {
+ return sym->size & 0x1f;
+ }
+
+ if (find_global_symbol (name) >= 0) {
+ return get_global_symbol_size (name) & 0x1f;
+ }
+
+ return DATA_INT & 0x1f;
+
+}
+
+static void inline_asm_emit_output_store (struct inline_asm_input_operand *operand) {
+
+ struct local_symbol *sym;
+
+ if (!operand || !operand->is_output || operand->output_in_memory || !operand->reg_name || !operand->output_name[0]) {
+ return;
+ }
+
+ sym = find_local_symbol (operand->output_name);
+
+ if (sym) {
+
+ if (sym->is_static && sym->static_label) {
+ emit_store_reg_to_global (sym->static_label, sym->size, operand->reg_name);
+ } else {
+ emit_store_reg_to_local (sym->offset, sym->size, operand->reg_name);
+ }
+
+ return;
+
+ }
+
+ if (find_global_symbol (operand->output_name) >= 0) {
+ emit_store_reg_to_global (operand->output_name, get_global_symbol_size (operand->output_name), operand->reg_name);
+ }
+
+}
+
static void inline_asm_emit_input_load (const char *constraint, int input_index, struct inline_asm_input_operand *inputs, const char *templ) {
char expr_text[64];
inputs[input_index].restore_reg = 0;
inputs[input_index].restore_name = 0;
+ inputs[input_index].reg_name = 0;
+ inputs[input_index].size = DATA_INT & 0x1f;
- if (strchr (constraint, 'N')) {
+ if (strchr (constraint, 'N') && (token_is_integer_constant_now (tok.kind) || strlen (constraint) == 1)) {
if (token_is_integer_constant_now (tok.kind)) {
if (reg32) {
- subst = inline_asm_reg_for_template (reg32);
+ inputs[input_index].reg_name = reg32;
+
+ if (tok.kind == TOK_IDENT && tok.ident) {
+ inputs[input_index].size = inline_asm_identifier_size (tok.ident);
+ }
+
+ subst = inline_asm_reg_for_size (reg32, inputs[input_index].size);
if (inline_asm_reg_needs_restore (reg32) && state->ofp) {
}
+static const char *inline_asm_operand_subst (struct inline_asm_input_operand *operand, int modifier) {
+
+ if (!operand) {
+ return "";
+ }
+
+ if (!modifier || !operand->reg_name) {
+ return operand->subst;
+ }
+
+ switch (modifier) {
+
+ case 'b':
+
+ return inline_asm_reg_for_size (operand->reg_name, 1);
+
+ case 'w':
+
+ return inline_asm_reg_for_size (operand->reg_name, 2);
+
+ case 'k':
+
+ return inline_asm_reg_for_size (operand->reg_name, 4);
+
+ case 'q':
+
+ return inline_asm_reg_for_size (operand->reg_name, 8);
+
+ default:
+
+ return operand->subst;
+
+ }
+
+}
+
+static void inline_asm_finish_operands (struct inline_asm_input_operand *inputs, int input_count) {
+
+ int i;
+
+ for (i = 0; i < input_count; i++) {
+ inline_asm_emit_output_store (&inputs[i]);
+ }
+
+ for (i = input_count - 1; i >= 0; i--) {
+
+ if (inputs[i].restore_reg && inputs[i].restore_name) {
+ fprintf (state->ofp, " pop %s\n", inputs[i].restore_name);
+ }
+
+ }
+
+}
+
static void inline_asm_emit_template (const char *templ, struct inline_asm_input_operand *inputs, int input_count) {
char line[512];
}
- } else if (*p == '%' && p[1] >= '0' && p[1] <= '9') {
+ } else if (*p == '%') {
- int index = p[1] - '0';
- const char *subst = "";
+ const char *q = p + 1;
+ const char *subst;
+
+ int index = 0;
+ int modifier = 0;
+ int have_index = 0;
+
+ if (*q == 'b' || *q == 'w' || *q == 'k' || *q == 'q') {
+
+ modifier = *q;
+ q++;
+
+ }
+
+ while (*q >= '0' && *q <= '9') {
+
+ have_index = 1;
+
+ index = index * 10 + (*q - '0');
+ q++;
+
+ }
+
+ if (!have_index) {
+
+ line[n++] = *p;
+ continue;
+
+ }
+
+ subst = "";
if (index >= 0 && index < input_count) {
- subst = inputs[index].subst;
+ subst = inline_asm_operand_subst (&inputs[index], modifier);
}
while (*subst && n + 1 < sizeof (line)) {
line[n++] = *subst++;
}
- p++;
+ p = q - 1;
} else {
line[n++] = *p;
}
fprintf (state->ofp, " out %s, %s\n", rhs, lhs);
-
- while (input_count-- > 0) {
-
- if (inputs[input_count].restore_reg && inputs[input_count].restore_name) {
- fprintf (state->ofp, " pop %s\n", inputs[input_count].restore_name);
- }
-
- }
+ inline_asm_finish_operands (inputs, input_count);
return;
}
- while (input_count-- > 0) {
-
- if (inputs[input_count].restore_reg && inputs[input_count].restore_name) {
- fprintf (state->ofp, " pop %s\n", inputs[input_count].restore_name);
- }
-
- }
+ inline_asm_finish_operands (inputs, input_count);
}
char templ[256];
- struct inline_asm_input_operand inputs[MAX_INLINE_ASM_INPUTS];
- int input_count = 0;
+ struct inline_asm_input_operand operands[MAX_INLINE_ASM_INPUTS];
+ int operand_count = 0;
if (tok.kind != TOK_ASM) {
return 0;
}
- memset (inputs, 0, sizeof (inputs));
-
+ memset (operands, 0, sizeof (operands));
get_token ();
+
+ /**
+ * GNU accepts both __asm__ volatile (...) and __asm__ __volatile__ (...).
+ * The lexer currently normalises the standard volatile spelling here.
+ */
+ if (tok.kind == TOK_VOLATILE) {
+ get_token ();
+ }
+
expect (TOK_LPAREN, "(");
if (!is_string_token ()) {
}
- inline_asm_unquote_string_token (templ, sizeof (templ));
- get_token ();
+ inline_asm_read_string_sequence (templ, sizeof (templ));
+ /* output operands */
if (_accept (TOK_COLON)) {
- if (tok.kind != TOK_COLON) {
- skip_balanced_until (TOK_COLON, TOK_RPAREN, TOK_EOF);
+ while (tok.kind != TOK_COLON && tok.kind != TOK_RPAREN && tok.kind != TOK_EOF) {
+
+ char constraint[16];
+ char name[64];
+ char memref[64];
+
+ const char *reg;
+ int read_write;
+
+ if (!is_string_token ()) {
+
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "expected asm output constraint string");
+
+ skip_balanced_until (TOK_COLON, TOK_RPAREN, TOK_EOF);
+ break;
+
+ }
+
+ inline_asm_unquote_string_token (constraint, sizeof (constraint));
+
+ get_token ();
+ expect (TOK_LPAREN, "(");
+
+ name[0] = 0;
+
+ if (tok.kind == TOK_IDENT && tok.ident) {
+ inline_copy_string (name, tok.ident, sizeof (name));
+ } else {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "asm output operand must be an identifier");
+ }
+
+ if (operand_count < MAX_INLINE_ASM_INPUTS) {
+
+ struct inline_asm_input_operand *op = &operands[operand_count];
+
+ inline_copy_string (op->constraint, constraint, sizeof (op->constraint));
+ inline_copy_string (op->output_name, name, sizeof (op->output_name));
+
+ op->is_output = 1;
+ op->size = inline_asm_identifier_size (name);
+
+ read_write = strchr (constraint, '+') != 0;
+ reg = inline_asm_constraint_reg32 (constraint);
+
+ if (strchr (constraint, 'm')) {
+
+ op->output_in_memory = 1;
+
+ if (inline_asm_format_identifier_operand (memref, sizeof (memref), name)) {
+ inline_copy_string (op->subst, memref, sizeof (op->subst));
+ }
+
+ skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+
+ } else if (reg) {
+
+ op->reg_name = reg;
+ inline_copy_string (op->subst, inline_asm_reg_for_size (reg, op->size), sizeof (op->subst));
+
+ if (inline_asm_reg_needs_restore (reg) && state->ofp) {
+
+ fprintf (state->ofp, " push %s\n", reg);
+
+ op->restore_reg = 1;
+ op->restore_name = reg;
+
+ }
+
+ if (read_write) {
+ emit_load_assignment_rhs_expression_to_reg (reg);
+ } else {
+ skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+ }
+
+ } else {
+
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "unsupported asm output constraint '%s'", constraint);
+ skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+
+ }
+
+ operand_count++;
+
+ } else {
+ skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+ }
+
+ expect (TOK_RPAREN, ")");
+
+ if (!_accept (TOK_COMMA)) {
+ break;
+ }
+
}
+ /* input operands */
if (_accept (TOK_COLON)) {
- while (tok.kind != TOK_RPAREN && tok.kind != TOK_EOF) {
+ while (tok.kind != TOK_COLON && tok.kind != TOK_RPAREN && tok.kind != TOK_EOF) {
char constraint[16];
report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "expected asm input constraint string");
- skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+ skip_balanced_until (TOK_COLON, TOK_RPAREN, TOK_EOF);
break;
}
expect (TOK_LPAREN, "(");
- if (input_count < MAX_INLINE_ASM_INPUTS) {
+ if (operand_count < MAX_INLINE_ASM_INPUTS) {
- inline_copy_string (inputs[input_count].constraint, constraint, sizeof (inputs[input_count].constraint));
- inline_asm_emit_input_load (constraint, input_count, inputs, templ);
+ inline_copy_string (operands[operand_count].constraint, constraint, sizeof (operands[operand_count].constraint));
+ inline_asm_emit_input_load (constraint, operand_count, operands, templ);
- input_count++;
+ operand_count++;
} else {
skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
}
}
+
+ /* clobbers */
+ if (_accept (TOK_COLON)) {
+
+ while (tok.kind != TOK_RPAREN && tok.kind != TOK_EOF) {
+
+ char clobber[16];
+ const char *save_reg = 0;
+
+ if (!is_string_token ()) {
+
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "expected asm clobber string");
+
+ skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+ break;
+
+ }
+
+ inline_asm_unquote_string_token (clobber, sizeof (clobber));
+ get_token ();
+
+ if (strcmp (clobber, "ebx") == 0 || strcmp (clobber, "rbx") == 0 || strcmp (clobber, "bx") == 0 || strcmp (clobber, "bl") == 0) {
+ save_reg = inline_asm_constraint_reg32 ("b");
+ } else if (strcmp (clobber, "esi") == 0 || strcmp (clobber, "rsi") == 0 || strcmp (clobber, "si") == 0) {
+ save_reg = inline_asm_constraint_reg32 ("S");
+ } else if (strcmp (clobber, "edi") == 0 || strcmp (clobber, "rdi") == 0 || strcmp (clobber, "di") == 0) {
+ save_reg = inline_asm_constraint_reg32 ("D");
+ }
+
+ if (save_reg && operand_count < MAX_INLINE_ASM_INPUTS && state->ofp) {
+
+ fprintf (state->ofp, " push %s\n", save_reg);
+
+ operands[operand_count].restore_reg = 1;
+ operands[operand_count].restore_name = save_reg;
+
+ operand_count++;
+
+ }
+
+ if (!_accept (TOK_COMMA)) {
+ break;
+ }
+
+ }
+
+ }
}
expect (TOK_RPAREN, ")");
expect (TOK_SEMI, ";");
- inline_asm_emit_template (templ, inputs, input_count);
+ inline_asm_emit_template (templ, operands, operand_count);
return 1;
}
char constraint[16];
char subst[64];
+ char output_name[64];
+
+ const char *reg_name;
+
+ int size;
+ int is_output;
+ int output_in_memory;
int restore_reg;
+
const char *restore_name;
};
}
+static void inline_asm_read_string_sequence (char *dst, size_t dst_size) {
+
+ char part[256];
+
+ size_t used;
+ size_t avail;
+ size_t part_len;
+
+ if (!dst || dst_size == 0) {
+ return;
+ }
+
+ dst[0] = 0;
+
+ while (is_string_token ()) {
+
+ inline_asm_unquote_string_token (part, sizeof (part));
+
+ used = strlen (dst);
+ avail = dst_size - used - 1;
+
+ if (avail) {
+
+ part_len = strlen (part);
+
+ if (part_len > avail) {
+ part_len = avail;
+ }
+
+ memcpy (dst + used, part, part_len);
+ dst[used + part_len] = 0;
+
+ }
+
+ get_token ();
+
+ }
+
+}
+
static const char *inline_asm_constraint_reg32 (const char *constraint) {
if (!constraint) {
return "edi";
}
+ if (strchr (constraint, 'r')) {
+ return "eax";
+ }
+
return 0;
}
-static const char *inline_asm_reg_for_template (const char *reg32) {
+static const char *inline_asm_reg_for_size (const char *reg, int size) {
- if (!reg32) {
+ if (!reg) {
return "";
}
- if (strcmp (reg32, "eax") == 0) {
- return "al";
+ if (strcmp (reg, "eax") == 0) {
+
+ if (size == 1) {
+ return "al";
+ }
+
+ if (size == 2) {
+ return "ax";
+ }
+
+ return "eax";
+
}
- if (strcmp (reg32, "ebx") == 0) {
- return "bl";
+ if (strcmp (reg, "ebx") == 0) {
+
+ if (size == 1) {
+ return "bl";
+ }
+
+ if (size == 2) {
+ return "bx";
+ }
+
+ return "ebx";
+
}
- if (strcmp (reg32, "ecx") == 0) {
- return "cl";
+ if (strcmp (reg, "ecx") == 0) {
+
+ if (size == 1) {
+ return "cl";
+ }
+
+ if (size == 2) {
+ return "cx";
+ }
+
+ return "ecx";
+
}
- if (strcmp (reg32, "edx") == 0) {
- return "dx";
+ if (strcmp (reg, "edx") == 0) {
+
+ if (size == 1) {
+ return "dl";
+ }
+
+ if (size == 2) {
+ return "dx";
+ }
+
+ return "edx";
+
}
- if (strcmp (reg32, "esi") == 0) {
+ if (strcmp (reg, "esi") == 0) {
+
+ if (size == 2) {
+ return "si";
+ }
+
return "esi";
+
}
- if (strcmp (reg32, "edi") == 0) {
+ if (strcmp (reg, "edi") == 0) {
+
+ if (size == 2) {
+ return "di";
+ }
+
return "edi";
+
}
- return reg32;
+ return reg;
}
}
+static int inline_asm_identifier_size (const char *name) {
+
+ struct local_symbol *sym;
+
+ if (!name || !*name) {
+ return DATA_INT & 0x1f;
+ }
+
+ sym = find_local_symbol (name);
+
+ if (sym) {
+ return sym->size & 0x1f;
+ }
+
+ if (find_global_symbol (name) >= 0) {
+ return get_global_symbol_size (name) & 0x1f;
+ }
+
+ return DATA_INT & 0x1f;
+
+}
+
+static void inline_asm_emit_output_store (struct inline_asm_input_operand *operand) {
+
+ struct local_symbol *sym;
+
+ if (!operand || !operand->is_output || operand->output_in_memory || !operand->reg_name || !operand->output_name[0]) {
+ return;
+ }
+
+ sym = find_local_symbol (operand->output_name);
+
+ if (sym) {
+
+ if (sym->is_static && sym->static_label) {
+ emit_store_reg_to_global (sym->static_label, sym->size, operand->reg_name);
+ } else {
+ emit_store_reg_to_local (sym->offset, sym->size, operand->reg_name);
+ }
+
+ return;
+
+ }
+
+ if (find_global_symbol (operand->output_name) >= 0) {
+ emit_store_reg_to_global (operand->output_name, get_global_symbol_size (operand->output_name), operand->reg_name);
+ }
+
+}
+
static void inline_asm_emit_input_load (const char *constraint, int input_index, struct inline_asm_input_operand *inputs, const char *templ) {
char expr_text[64];
inputs[input_index].restore_reg = 0;
inputs[input_index].restore_name = 0;
+ inputs[input_index].reg_name = 0;
+ inputs[input_index].size = DATA_INT & 0x1f;
- if (strchr (constraint, 'N')) {
+ if (strchr (constraint, 'N') && (token_is_integer_constant_now (tok.kind) || strlen (constraint) == 1)) {
if (token_is_integer_constant_now (tok.kind)) {
if (reg32) {
- subst = inline_asm_reg_for_template (reg32);
+ inputs[input_index].reg_name = reg32;
+
+ if (tok.kind == TOK_IDENT && tok.ident) {
+ inputs[input_index].size = inline_asm_identifier_size (tok.ident);
+ }
+
+ subst = inline_asm_reg_for_size (reg32, inputs[input_index].size);
if (inline_asm_reg_needs_restore (reg32) && state->ofp) {
}
+static const char *inline_asm_operand_subst (struct inline_asm_input_operand *operand, int modifier) {
+
+ if (!operand) {
+ return "";
+ }
+
+ if (!modifier || !operand->reg_name) {
+ return operand->subst;
+ }
+
+ switch (modifier) {
+
+ case 'b':
+
+ return inline_asm_reg_for_size (operand->reg_name, 1);
+
+ case 'w':
+
+ return inline_asm_reg_for_size (operand->reg_name, 2);
+
+ case 'k':
+
+ return inline_asm_reg_for_size (operand->reg_name, 4);
+
+ default:
+
+ return operand->subst;
+
+ }
+
+}
+
+static void inline_asm_finish_operands (struct inline_asm_input_operand *inputs, int input_count) {
+
+ int i;
+
+ for (i = 0; i < input_count; i++) {
+ inline_asm_emit_output_store (&inputs[i]);
+ }
+
+ for (i = input_count - 1; i >= 0; i--) {
+
+ if (inputs[i].restore_reg && inputs[i].restore_name) {
+ fprintf (state->ofp, " pop %s\n", inputs[i].restore_name);
+ }
+
+ }
+
+}
+
static void inline_asm_emit_template (const char *templ, struct inline_asm_input_operand *inputs, int input_count) {
char line[512];
}
- } else if (*p == '%' && p[1] >= '0' && p[1] <= '9') {
+ } else if (*p == '%') {
- int index = p[1] - '0';
- const char *subst = "";
+ const char *q = p + 1;
+ const char *subst;
+
+ int index = 0;
+ int modifier = 0;
+ int have_index = 0;
+
+ if (*q == 'b' || *q == 'w' || *q == 'k' || *q == 'q') {
+
+ modifier = *q;
+ q++;
+
+ }
+
+ while (*q >= '0' && *q <= '9') {
+
+ have_index = 1;
+
+ index = index * 10 + (*q - '0');
+ q++;
+
+ }
+
+ if (!have_index) {
+
+ line[n++] = *p;
+ continue;
+
+ }
+
+ subst = "";
if (index >= 0 && index < input_count) {
- subst = inputs[index].subst;
+ subst = inline_asm_operand_subst (&inputs[index], modifier);
}
while (*subst && n + 1 < sizeof (line)) {
line[n++] = *subst++;
}
- p++;
+ p = q - 1;
} else {
line[n++] = *p;
}
fprintf (state->ofp, " out %s, %s\n", rhs, lhs);
-
- while (input_count-- > 0) {
-
- if (inputs[input_count].restore_reg && inputs[input_count].restore_name) {
- fprintf (state->ofp, " pop %s\n", inputs[input_count].restore_name);
- }
-
- }
+ inline_asm_finish_operands (inputs, input_count);
return;
}
- while (input_count-- > 0) {
-
- if (inputs[input_count].restore_reg && inputs[input_count].restore_name) {
- fprintf (state->ofp, " pop %s\n", inputs[input_count].restore_name);
- }
-
- }
+ inline_asm_finish_operands (inputs, input_count);
}
char templ[256];
- struct inline_asm_input_operand inputs[MAX_INLINE_ASM_INPUTS];
- int input_count = 0;
+ struct inline_asm_input_operand operands[MAX_INLINE_ASM_INPUTS];
+ int operand_count = 0;
if (tok.kind != TOK_ASM) {
return 0;
}
- memset (inputs, 0, sizeof (inputs));
-
+ memset (operands, 0, sizeof (operands));
get_token ();
+
+ /**
+ * GNU accepts both __asm__ volatile (...) and __asm__ __volatile__ (...).
+ * The lexer currently normalises the standard volatile spelling here.
+ */
+ if (tok.kind == TOK_VOLATILE) {
+ get_token ();
+ }
+
expect (TOK_LPAREN, "(");
if (!is_string_token ()) {
}
- inline_asm_unquote_string_token (templ, sizeof (templ));
- get_token ();
+ inline_asm_read_string_sequence (templ, sizeof (templ));
+ /* output operands */
if (_accept (TOK_COLON)) {
- if (tok.kind != TOK_COLON) {
- skip_balanced_until (TOK_COLON, TOK_RPAREN, TOK_EOF);
+ while (tok.kind != TOK_COLON && tok.kind != TOK_RPAREN && tok.kind != TOK_EOF) {
+
+ char constraint[16];
+ char name[64];
+ char memref[64];
+
+ const char *reg;
+ int read_write;
+
+ if (!is_string_token ()) {
+
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "expected asm output constraint string");
+
+ skip_balanced_until (TOK_COLON, TOK_RPAREN, TOK_EOF);
+ break;
+
+ }
+
+ inline_asm_unquote_string_token (constraint, sizeof (constraint));
+
+ get_token ();
+ expect (TOK_LPAREN, "(");
+
+ name[0] = 0;
+
+ if (tok.kind == TOK_IDENT && tok.ident) {
+ inline_copy_string (name, tok.ident, sizeof (name));
+ } else {
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "asm output operand must be an identifier");
+ }
+
+ if (operand_count < MAX_INLINE_ASM_INPUTS) {
+
+ struct inline_asm_input_operand *op = &operands[operand_count];
+
+ inline_copy_string (op->constraint, constraint, sizeof (op->constraint));
+ inline_copy_string (op->output_name, name, sizeof (op->output_name));
+
+ op->is_output = 1;
+ op->size = inline_asm_identifier_size (name);
+
+ read_write = strchr (constraint, '+') != 0;
+ reg = inline_asm_constraint_reg32 (constraint);
+
+ if (strchr (constraint, 'm')) {
+
+ op->output_in_memory = 1;
+
+ if (inline_asm_format_identifier_operand (memref, sizeof (memref), name)) {
+ inline_copy_string (op->subst, memref, sizeof (op->subst));
+ }
+
+ skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+
+ } else if (reg) {
+
+ op->reg_name = reg;
+ inline_copy_string (op->subst, inline_asm_reg_for_size (reg, op->size), sizeof (op->subst));
+
+ if (inline_asm_reg_needs_restore (reg) && state->ofp) {
+
+ fprintf (state->ofp, " push %s\n", reg);
+
+ op->restore_reg = 1;
+ op->restore_name = reg;
+
+ }
+
+ if (read_write) {
+ emit_load_assignment_rhs_expression_to_reg (reg);
+ } else {
+ skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+ }
+
+ } else {
+
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "unsupported asm output constraint '%s'", constraint);
+ skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+
+ }
+
+ operand_count++;
+
+ } else {
+ skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+ }
+
+ expect (TOK_RPAREN, ")");
+
+ if (!_accept (TOK_COMMA)) {
+ break;
+ }
+
}
+ /* input operands */
if (_accept (TOK_COLON)) {
- while (tok.kind != TOK_RPAREN && tok.kind != TOK_EOF) {
+ while (tok.kind != TOK_COLON && tok.kind != TOK_RPAREN && tok.kind != TOK_EOF) {
char constraint[16];
report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "expected asm input constraint string");
- skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+ skip_balanced_until (TOK_COLON, TOK_RPAREN, TOK_EOF);
break;
}
expect (TOK_LPAREN, "(");
- if (input_count < MAX_INLINE_ASM_INPUTS) {
+ if (operand_count < MAX_INLINE_ASM_INPUTS) {
- inline_copy_string (inputs[input_count].constraint, constraint, sizeof (inputs[input_count].constraint));
- inline_asm_emit_input_load (constraint, input_count, inputs, templ);
+ inline_copy_string (operands[operand_count].constraint, constraint, sizeof (operands[operand_count].constraint));
+ inline_asm_emit_input_load (constraint, operand_count, operands, templ);
- input_count++;
+ operand_count++;
} else {
skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
}
}
+
+ /* clobbers */
+ if (_accept (TOK_COLON)) {
+
+ while (tok.kind != TOK_RPAREN && tok.kind != TOK_EOF) {
+
+ char clobber[16];
+ const char *save_reg = 0;
+
+ if (!is_string_token ()) {
+
+ report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "expected asm clobber string");
+
+ skip_balanced_until (TOK_RPAREN, TOK_EOF, TOK_EOF);
+ break;
+
+ }
+
+ inline_asm_unquote_string_token (clobber, sizeof (clobber));
+ get_token ();
+
+ if (strcmp (clobber, "ebx") == 0 || strcmp (clobber, "rbx") == 0 || strcmp (clobber, "bx") == 0 || strcmp (clobber, "bl") == 0) {
+ save_reg = inline_asm_constraint_reg32 ("b");
+ } else if (strcmp (clobber, "esi") == 0 || strcmp (clobber, "rsi") == 0 || strcmp (clobber, "si") == 0) {
+ save_reg = inline_asm_constraint_reg32 ("S");
+ } else if (strcmp (clobber, "edi") == 0 || strcmp (clobber, "rdi") == 0 || strcmp (clobber, "di") == 0) {
+ save_reg = inline_asm_constraint_reg32 ("D");
+ }
+
+ if (save_reg && operand_count < MAX_INLINE_ASM_INPUTS && state->ofp) {
+
+ fprintf (state->ofp, " push %s\n", save_reg);
+
+ operands[operand_count].restore_reg = 1;
+ operands[operand_count].restore_name = save_reg;
+
+ operand_count++;
+
+ }
+
+ if (!_accept (TOK_COMMA)) {
+ break;
+ }
+
+ }
+
+ }
}
expect (TOK_RPAREN, ")");
expect (TOK_SEMI, ";");
- inline_asm_emit_template (templ, inputs, input_count);
+ inline_asm_emit_template (templ, operands, operand_count);
return 1;
}
{ "__asm__", 0, VERSION, TOK_ASM },
{ "__inline__", 0, VERSION, TOK_INLINE },
{ "__restrict__", 0, VERSION, TOK_RESTRICT },
+ { "__volatile__", 0, VERSION, TOK_VOLATILE },
/* C90 keywords */
{ "auto", 90, VERSION, TOK_AUTO },