Added GCC extended asm support
authorRobert Pengelly <robertapengelly@hotmail.com>
Mon, 24 Aug 2026 08:35:12 +0000 (09:35 +0100)
committerRobert Pengelly <robertapengelly@hotmail.com>
Mon, 24 Aug 2026 08:35:12 +0000 (09:35 +0100)
EXTENSIONS.md
amd64.c
i386.c
token.c

index e01b57a13bcf70b0544156ed8a61baaaa6110267..a23b6e1431a30dc47125f6deb040ae3ed94fc09e 100644 (file)
@@ -23,9 +23,9 @@ reserved for future compiler-specific features.
 
 ## `__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.
diff --git a/amd64.c b/amd64.c
index ae021f9674d122d1b117a969009f0d6e4308463d..4308f833be653fd36d83a609b916f346e185f6b2 100644 (file)
--- a/amd64.c
+++ b/amd64.c
@@ -32091,7 +32091,15 @@ struct inline_asm_input_operand {
 
     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;
 
 };
@@ -32209,6 +32217,46 @@ static void inline_asm_unquote_string_token (char *dst, size_t dst_size) {
 
 }
 
+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) {
@@ -32239,41 +32287,129 @@ static const char *inline_asm_constraint_reg32 (const char *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;
 
 }
 
@@ -32474,6 +32610,56 @@ static int inline_asm_format_identifier_operand (char *dst, size_t dst_size, con
 
 }
 
+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];
@@ -32488,8 +32674,10 @@ static void inline_asm_emit_input_load (const char *constraint, int input_index,
     
     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)) {
         
@@ -32541,7 +32729,13 @@ static void inline_asm_emit_input_load (const char *constraint, int input_index,
     
     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) {
         
@@ -32589,6 +32783,60 @@ static void inline_asm_emit_input_load (const char *constraint, int input_index,
 
 }
 
+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];
@@ -32613,20 +32861,49 @@ static void inline_asm_emit_template (const char *templ, struct inline_asm_input
             
             }
         
-        } 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;
@@ -32666,14 +32943,7 @@ static void inline_asm_emit_template (const char *templ, struct inline_asm_input
                 }
                 
                 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;
             
@@ -32690,13 +32960,7 @@ static void inline_asm_emit_template (const char *templ, struct inline_asm_input
     
     }
     
-    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);
 
 }
 
@@ -32704,16 +32968,24 @@ static int parse_inline_asm_statement (void) {
 
     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 ()) {
@@ -32727,18 +32999,110 @@ static int parse_inline_asm_statement (void) {
     
     }
     
-    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];
                 
@@ -32746,7 +33110,7 @@ static int parse_inline_asm_statement (void) {
                 
                     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;
                 
                 }
@@ -32756,12 +33120,12 @@ static int parse_inline_asm_statement (void) {
                 
                 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);
@@ -32774,6 +33138,53 @@ static int parse_inline_asm_statement (void) {
                 }
             
             }
+            
+            /* 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;
+                    }
+                
+                }
+            
+            }
         
         }
     
@@ -32782,7 +33193,7 @@ static int parse_inline_asm_statement (void) {
     expect (TOK_RPAREN, ")");
     expect (TOK_SEMI, ";");
     
-    inline_asm_emit_template (templ, inputs, input_count);
+    inline_asm_emit_template (templ, operands, operand_count);
     return 1;
 
 }
diff --git a/i386.c b/i386.c
index 61b446425b8060677d4db152649f7465beffa207..3f08288aae2b11c87c8c822bf035e67bef05a590 100644 (file)
--- a/i386.c
+++ b/i386.c
@@ -29397,7 +29397,15 @@ struct inline_asm_input_operand {
 
     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;
 
 };
@@ -29515,6 +29523,46 @@ static void inline_asm_unquote_string_token (char *dst, size_t dst_size) {
 
 }
 
+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) {
@@ -29545,41 +29593,97 @@ static const char *inline_asm_constraint_reg32 (const char *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;
 
 }
 
@@ -29780,6 +29884,56 @@ static int inline_asm_format_identifier_operand (char *dst, size_t dst_size, con
 
 }
 
+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];
@@ -29794,8 +29948,10 @@ static void inline_asm_emit_input_load (const char *constraint, int input_index,
     
     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)) {
         
@@ -29847,7 +30003,13 @@ static void inline_asm_emit_input_load (const char *constraint, int input_index,
     
     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) {
         
@@ -29895,6 +30057,56 @@ static void inline_asm_emit_input_load (const char *constraint, int input_index,
 
 }
 
+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];
@@ -29919,20 +30131,49 @@ static void inline_asm_emit_template (const char *templ, struct inline_asm_input
             
             }
         
-        } 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;
@@ -29972,14 +30213,7 @@ static void inline_asm_emit_template (const char *templ, struct inline_asm_input
                 }
                 
                 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;
             
@@ -29996,13 +30230,7 @@ static void inline_asm_emit_template (const char *templ, struct inline_asm_input
     
     }
     
-    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);
 
 }
 
@@ -30010,16 +30238,24 @@ static int parse_inline_asm_statement (void) {
 
     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 ()) {
@@ -30033,18 +30269,110 @@ static int parse_inline_asm_statement (void) {
     
     }
     
-    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];
                 
@@ -30052,7 +30380,7 @@ static int parse_inline_asm_statement (void) {
                 
                     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;
                 
                 }
@@ -30062,12 +30390,12 @@ static int parse_inline_asm_statement (void) {
                 
                 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);
@@ -30080,6 +30408,53 @@ static int parse_inline_asm_statement (void) {
                 }
             
             }
+            
+            /* 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;
+                    }
+                
+                }
+            
+            }
         
         }
     
@@ -30088,7 +30463,7 @@ static int parse_inline_asm_statement (void) {
     expect (TOK_RPAREN, ")");
     expect (TOK_SEMI, ";");
     
-    inline_asm_emit_template (templ, inputs, input_count);
+    inline_asm_emit_template (templ, operands, operand_count);
     return 1;
 
 }
diff --git a/token.c b/token.c
index aa0ca17972c94eabeab179402bb294daf93b4cbe..cee195ebb45cc45561826e2c3e477d5139fa009c 100755 (executable)
--- a/token.c
+++ b/token.c
@@ -1077,6 +1077,7 @@ static int find_kind (const char *start, const char *caret, const char *p) {
         {   "__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                },