More AMD64 fixes
authorRobert Pengelly <robertapengelly@hotmail.com>
Fri, 19 Jun 2026 18:41:18 +0000 (19:41 +0100)
committerRobert Pengelly <robertapengelly@hotmail.com>
Fri, 19 Jun 2026 18:41:18 +0000 (19:41 +0100)
amd64.c
parse.c

diff --git a/amd64.c b/amd64.c
index fb6fbe8a02caaa15e4109546081118edebc1a23f..17fe957734289b9749a0b57745515cdf1c8e2c39 100644 (file)
--- a/amd64.c
+++ b/amd64.c
@@ -311,7 +311,14 @@ static void save_typedef_name (const char *name, int size, int is_unsigned, int
     }
     
     if (is_aggregate && !is_array) {
-        size = amd64_relayout_aggregate_members (size, parsed_type_tag_name[0] ? parsed_type_tag_name : 0, name);
+    
+        const char *owner_tag_name = parsed_type_tag_name[0] ? parsed_type_tag_name : 0;
+        int fixed_size = amd64_relayout_aggregate_members (size, owner_tag_name, name);
+        
+        if (fixed_size > 0) {
+            size = fixed_size;
+        }
+    
     }
     
     entry = find_typedef_name (name);
@@ -1443,19 +1450,15 @@ static void append_inline_text (char **dst, const char *start, size_t len);
 static long function_frame_saved_assignment64_bytes (void) {
 
     /*
-     * Deferred frame restoration only knows whether assignment64 register
-     * preservation was requested, not which operator triggered it.
-     *
-     * emit_preserve_assignment64_regs() saves:
-     *   - RBX only for most operators (8 bytes)
-     *   - RBX+RSI+RDI for multiply/divide/modulo paths (24 bytes)
-     *
-     * Returning a hard-coded 24 here over-allocates the frame and causes the
-     * deferred epilogue to restore registers that were never saved.
+     * If the deferred frame path needs any callee-saved scratch register,
+     * save a fixed 32-byte set: RBX, RSI, RDI and R12.
      *
-     * Until the exact save set is tracked, defer only the always-saved RBX.
+     * The previous deferred-preserve patch saved either 8 bytes or 24 bytes
+     * and placed those pushes before the local-frame allocation.  That lets
+     * saved RBX occupy [RBP - 8], which is also where the first compiler local
+     * can live, and the 24-byte save case also breaks 16-byte call alignment.
      */
-    return current_function_preserve_assignment64_regs == 2 ? 24 : (current_function_preserve_assignment64_regs ? 8 : 0);
+    return current_function_preserve_assignment64_regs ? 32 : 0;
 
 }
 
@@ -1463,10 +1466,8 @@ static void emit_function_frame_adjust_text (char **dst, long frame_size) {
 
     char buf[128];
     
-    long save_bytes;
-    long total_frame_bytes;
-    
     int n;
+    long save_bytes;
     
     if (!dst) {
         return;
@@ -1474,24 +1475,18 @@ static void emit_function_frame_adjust_text (char **dst, long frame_size) {
     
     save_bytes = function_frame_saved_assignment64_bytes ();
     
-    /*
-     * AMD64 calls must see RSP 16-byte aligned at the call instruction.
-     * After the usual "push rbp" prologue, RSP is aligned.  Any local
-     * frame allocation plus any callee-save pushes emitted here must therefore
-     * consume a multiple of 16 bytes in total.
-     *
-     * The old code rounded only the local frame to 4 bytes and ignored the
-     * optional rbx/rsi/rdi saves.  With those three pushes, a function with no
-     * locals consumed 24 bytes and every later call was misaligned.
-     */
     if (frame_size < 0) {
         frame_size = 0;
     }
     
-    total_frame_bytes = frame_size + save_bytes;
-    total_frame_bytes = (total_frame_bytes + 15) & ~15L;
-    
-    frame_size = total_frame_bytes - save_bytes;
+    /*
+     * Locals are addressed from RBP downward.  Do not push saved registers
+     * immediately below RBP, because that collides with local slots such as
+     * [RBP - 8].  Reserve the local frame first, then save callee-saved
+     * registers below it.  The save set is fixed at 32 bytes, so RSP remains
+     * 16-byte aligned before calls.
+     */
+    frame_size = (frame_size + 15) & ~15L;
     
     if (frame_size <= 0 && save_bytes <= 0) {
         return;
@@ -1503,45 +1498,37 @@ static void emit_function_frame_adjust_text (char **dst, long frame_size) {
     
     if (state->syntax & ASM_SYNTAX_INTEL) {
     
-        if (save_bytes) {
+        if (frame_size > 0) {
         
-            append_inline_text (dst, "    push rbx\n", strlen ("    push rbx\n"));
-            
-            if (save_bytes == 24) {
-            
-                append_inline_text (dst, "    push rsi\n", strlen ("    push rsi\n"));
-                append_inline_text (dst, "    push rdi\n", strlen ("    push rdi\n"));
-            
-            }
+            n = sprintf (buf, "    sub rsp, %ld\n", frame_size);
+            append_inline_text (dst, buf, (size_t) n);
         
         }
         
-        if (frame_size > 0) {
+        if (save_bytes) {
         
-            n = sprintf (buf, "    sub rsp, %ld\n", frame_size);
-            append_inline_text (dst, buf, (size_t) n);
+            append_inline_text (dst, "    push rbx\n", strlen ("    push rbx\n"));
+            append_inline_text (dst, "    push rsi\n", strlen ("    push rsi\n"));
+            append_inline_text (dst, "    push rdi\n", strlen ("    push rdi\n"));
+            append_inline_text (dst, "    push r12\n", strlen ("    push r12\n"));
         
         }
     
     } else {
     
-        if (save_bytes) {
+        if (frame_size > 0) {
         
-            append_inline_text (dst, "    pushq %rbx\n", strlen ("    pushq %rbx\n"));
-            
-            if (save_bytes == 24) {
-            
-                append_inline_text (dst, "    pushq %rsi\n", strlen ("    pushq %rsi\n"));
-                append_inline_text (dst, "    pushq %rdi\n", strlen ("    pushq %rdi\n"));
-            
-            }
+            n = sprintf (buf, "    subq $%ld, %%rsp\n", frame_size);
+            append_inline_text (dst, buf, (size_t) n);
         
         }
         
-        if (frame_size > 0) {
+        if (save_bytes) {
         
-            n = sprintf (buf, "    subq $%ld, %%rsp\n", frame_size);
-            append_inline_text (dst, buf, (size_t) n);
+            append_inline_text (dst, "    pushq %rbx\n", strlen ("    pushq %rbx\n"));
+            append_inline_text (dst, "    pushq %rsi\n", strlen ("    pushq %rsi\n"));
+            append_inline_text (dst, "    pushq %rdi\n", strlen ("    pushq %rdi\n"));
+            append_inline_text (dst, "    pushq %r12\n", strlen ("    pushq %r12\n"));
         
         }
     
@@ -1553,10 +1540,8 @@ static void emit_function_frame_restore_text (char **dst, long frame_size) {
 
     char buf[128];
     
-    long save_bytes;
-    long total_frame_bytes;
-    
     int n;
+    long save_bytes;
     
     if (!dst) {
         return;
@@ -1568,10 +1553,7 @@ static void emit_function_frame_restore_text (char **dst, long frame_size) {
         frame_size = 0;
     }
     
-    total_frame_bytes = frame_size + save_bytes;
-    total_frame_bytes = (total_frame_bytes + 15) & ~15L;
-    
-    frame_size = total_frame_bytes - save_bytes;
+    frame_size = (frame_size + 15) & ~15L;
     
     if (frame_size > 2147483647L) {
         frame_size = 2147483647L;
@@ -1579,45 +1561,37 @@ static void emit_function_frame_restore_text (char **dst, long frame_size) {
     
     if (state->syntax & ASM_SYNTAX_INTEL) {
     
-        if (frame_size > 0) {
+        if (save_bytes) {
         
-            n = sprintf (buf, "    add rsp, %ld\n", frame_size);
-            append_inline_text (dst, buf, (size_t) n);
+            append_inline_text (dst, "    pop r12\n", strlen ("    pop r12\n"));
+            append_inline_text (dst, "    pop rdi\n", strlen ("    pop rdi\n"));
+            append_inline_text (dst, "    pop rsi\n", strlen ("    pop rsi\n"));
+            append_inline_text (dst, "    pop rbx\n", strlen ("    pop rbx\n"));
         
         }
         
-        if (save_bytes) {
+        if (frame_size > 0) {
         
-            if (save_bytes == 24) {
-            
-                append_inline_text (dst, "    pop rdi\n", strlen ("    pop rdi\n"));
-                append_inline_text (dst, "    pop rsi\n", strlen ("    pop rsi\n"));
-            
-            }
-            
-            append_inline_text (dst, "    pop rbx\n", strlen ("    pop rbx\n"));
+            n = sprintf (buf, "    add rsp, %ld\n", frame_size);
+            append_inline_text (dst, buf, (size_t) n);
         
         }
     
     } else {
     
-        if (frame_size > 0) {
+        if (save_bytes) {
         
-            n = sprintf (buf, "    addq $%ld, %%rsp\n", frame_size);
-            append_inline_text (dst, buf, (size_t) n);
+            append_inline_text (dst, "    popq %r12\n", strlen ("    popq %r12\n"));
+            append_inline_text (dst, "    popq %rdi\n", strlen ("    popq %rdi\n"));
+            append_inline_text (dst, "    popq %rsi\n", strlen ("    popq %rsi\n"));
+            append_inline_text (dst, "    popq %rbx\n", strlen ("    popq %rbx\n"));
         
         }
         
-        if (save_bytes) {
+        if (frame_size > 0) {
         
-            if (save_bytes == 24) {
-            
-                append_inline_text (dst, "    popq %rdi\n", strlen ("    popq %rdi\n"));
-                append_inline_text (dst, "    popq %rsi\n", strlen ("    popq %rsi\n"));
-            
-            }
-            
-            append_inline_text (dst, "    popq %rbx\n", strlen ("    popq %rbx\n"));
+            n = sprintf (buf, "    addq $%ld, %%rsp\n", frame_size);
+            append_inline_text (dst, buf, (size_t) n);
         
         }
     
@@ -6159,6 +6133,24 @@ struct local_init {
 
 };
 
+#define     MAX_PENDING_STRING_LITERALS                 4096
+
+struct pending_string_literal {
+
+    char *label;
+    int elem_size;
+    int value_count;
+    int64_s *values;
+
+};
+
+static struct pending_string_literal pending_string_literals[MAX_PENDING_STRING_LITERALS];
+static int pending_string_literal_count = 0;
+
+static void clear_pending_string_literals (void);
+static void queue_string_literal_global (const char *label, const int64_s *values, int value_count, int elem_size);
+static void emit_pending_string_literals (void);
+
 static void switch_section (int sec) {
 
     if (!state->ofp || current_section == sec) {
@@ -7928,6 +7920,20 @@ static void make_declarator_fields (int *out_fields, int *out_count, const int *
     
     *out_count = 0;
     
+    if (base_is_aggregate && !declarator_is_pointer) {
+
+        const char *owner_tag_name = parsed_type_tag_name[0] ? parsed_type_tag_name : 0;
+        int fixed_size = amd64_relayout_aggregate_members (base_size, owner_tag_name, 0);
+        
+        if (fixed_size > 0) {
+        
+            base_size = fixed_size;
+            parsed_type_size = fixed_size;
+        
+        }
+    
+    }
+    
     if (declarator_is_pointer) {
     
         out_fields[(*out_count)++] = DATA_PTR;
@@ -12256,6 +12262,30 @@ static void emit_load_postfix_lvalue_address_to_pair_ex_now (const char *lo, con
         return;
     }
     
+    /*
+     * Some postfix member paths already leave a scalar qword member value in
+     * LO, rather than the address of the member.  Do not dereference that
+     * scalar again when widening it for the 64-bit expression path; that
+     * produced bad sequences such as:
+     *
+     *     mov rax, qword ptr [rax + 40]   ; p->size
+     *     movsxd rax, dword ptr [rax]     ; bogus: treat size as pointer
+     *
+     * Pointer members still have postfix_member_pointer_depth > 0 and must
+     * continue through the normal lvalue-address load below.
+     */
+    if (postfix_member_seen && postfix_member_pointer_depth == 0 && (postfix_member_size == (DATA_PTR & 0x1f) || postfix_member_size == (DATA_LLONG & 0x1f))) {
+    
+        if (state->syntax & ASM_SYNTAX_INTEL) {
+            fprintf (state->ofp, "    xor %s, %s\n", hi, hi);
+        } else {
+            fprintf (state->ofp, "    xorq %%%s, %%%s\n", hi, hi);
+        }
+        
+        return;
+    
+    }
+    
     if (size > (DATA_PTR & 0x1f)) {
     
         emit_push_reg_now (lo);
@@ -12428,7 +12458,21 @@ static void emit_load_assignment_rhs_to_pair (const char *lo, const char *hi) {
                     
                     if (applied_postfix && postfix_member_seen) {
                     
-                        if (postfix_member_size == (DATA_LLONG & 0x1f) && !postfix_member_is_floating) {
+                        /*
+                         * emit_apply_postfix_member_access_to_reg_now() has
+                         * already loaded scalar member values into LO.  Do not
+                         * treat a 64-bit scalar member value as an address and
+                         * load from [LO] again; that generated sequences like:
+                         *
+                         *     mov rax, qword ptr [rax + 40]
+                         *     movsxd rax, dword ptr [rax]
+                         *
+                         * for current->size.  Only real pointer members should
+                         * be left as pointer values for later operators.
+                         */
+                        if (postfix_member_size == (DATA_LLONG & 0x1f) && !postfix_member_is_floating && postfix_member_pointer_depth == 0) {
+                            /* LO already contains the full 64-bit scalar. */
+                        } else if (postfix_member_size == (DATA_LLONG & 0x1f) && !postfix_member_is_floating) {
                         
                             const char *addr_reg = (strcmp (lo, "rcx") != 0 && strcmp (hi, "rcx") != 0) ? "rcx" : "rsi";
                             
@@ -12483,7 +12527,14 @@ static void emit_load_assignment_rhs_to_pair (const char *lo, const char *hi) {
             
             if (applied_postfix && postfix_member_seen) {
             
-                if (postfix_member_size == (DATA_LLONG & 0x1f) && !postfix_member_is_floating) {
+                /*
+                 * Scalar 64-bit members are already loaded by the postfix
+                 * member path.  Loading again through LO dereferences the
+                 * scalar value as if it were a pointer.
+                 */
+                if (postfix_member_size == (DATA_LLONG & 0x1f) && !postfix_member_is_floating && postfix_member_pointer_depth == 0) {
+                    /* LO already contains the full 64-bit scalar. */
+                } else if (postfix_member_size == (DATA_LLONG & 0x1f) && !postfix_member_is_floating) {
                 
                     const char *addr_reg = (strcmp (lo, "rcx") != 0 && strcmp (hi, "rcx") != 0) ? "rcx" : "rsi";
                     
@@ -16740,7 +16791,15 @@ static int emit_load_parenthesized_indirect_member_to_reg_now (const char *reg)
     
     free (name);
     
-    emit_load_deref_reg_now (reg, DATA_PTR);
+    /*
+     * For an expression of the form (*p).member, p already contains the
+     * address of the aggregate object.  The old AMD64 path loaded qword [p]
+     * here, treating the first field of the aggregate as another pointer.
+     * That turned scalar member loads into a second bogus dereference, e.g.
+     *     mov rax, qword ptr [rax + 40]
+     *     movsxd rax, dword ptr [rax]
+     * instead of loading the member directly from p + offset.
+     */
     first_member = 1;
     
     last_member_size = DATA_INT & 0x1f;
@@ -23156,7 +23215,6 @@ static int emit_push_global_aggregate_argument_now (const char *name) {
 static void emit_call_pointer_in_reg_now (const char *fn_reg, const char *result_reg) {
 
     int argc = 0;
-    int total_arg_bytes = 0;
     int arg_bytes;
     int arg_is_floating;
     int i;
@@ -23352,8 +23410,6 @@ static void emit_call_pointer_in_reg_now (const char *fn_reg, const char *result
             
             }
             
-            total_arg_bytes += arg_bytes;
-            
             if (arg_saved_ofp) {
             
                 fflush (arg_tmp_ofp);
@@ -26332,40 +26388,22 @@ static int emit_load_assignment_binary_expression_prec_to_reg (const char *reg,
     }
     
     /*
-     * The postfix member helper deliberately leaves aggregates and 64-bit
-     * scalar members as an address because the pair/aggregate paths may need
-     * the full object.  This single-register expression path, however, only
-     * has EAX/EDX-style 32-bit arithmetic available.  When such a member is
-     * used inside ordinary arithmetic/comparison code (for example the
-     * CHECK_READ macro in pdld with address_type fields), load the low word
-     * from the member address instead of adding the member address itself.
+     * Scalar 64-bit postfix members are already values in REG.
+     *
+     * Do not collapse them by loading through REG when an arithmetic operator
+     * follows.  That was still producing:
+     *
+     *     mov rax, qword ptr [rax + 40]   ; p->size value
+     *     movsxd rax, dword ptr [rax]     ; bogus dereference of size
+     *
+     * for expressions such as:
+     *
+     *     p->size - bytes
+     *     n->size + p->size
+     *
+     * The normal arithmetic path below can operate on the qword value already
+     * held in REG.
      */
-    if (postfix_member_seen
-        && postfix_member_pointer_depth == 0
-        && postfix_member_size == (DATA_LLONG & 0x1f)
-        && !postfix_member_is_floating
-        && is_arithmetic_binary_operator (tok.kind)) {
-
-        /*
-         * Only collapse a pending 64-bit member address to a 32-bit scalar
-         * when this path is actually about to perform old 32-bit arithmetic
-         * on it.  For a plain rvalue/call argument such as:
-         *
-         *     LoadedImage->DeviceHandle
-         *
-         * the member load has already produced the qword value in REG.  Loading
-         * through that value again turns an EFI_HANDLE into:
-         *
-         *     mov rax, qword ptr [rax + 24]
-         *     movsxd rax, dword ptr [rax]
-         *
-         * which dereferences the handle and breaks HandleProtocol.
-         */
-        emit_load_deref_reg_now (reg, DATA_INT & 0x1f);
-        postfix_member_size = DATA_INT & 0x1f;
-    
-    }
-    
     if (postfix_member_seen && postfix_member_is_unsigned) {
         is_unsigned = 1;
     }
@@ -27004,6 +27042,7 @@ static void emit_assignment64_logical_op_to_pair_now (enum token_kind op, const
 
     int true_label;
     int false_label;
+    int rhs_label;
     int end_label;
     
     if (!state->ofp) {
@@ -27012,79 +27051,73 @@ static void emit_assignment64_logical_op_to_pair_now (enum token_kind op, const
     
     true_label = anon_label++;
     false_label = anon_label++;
+    rhs_label = anon_label++;
     end_label = anon_label++;
     
+    /*
+     * AMD64 scalar 64-bit values and pointers live in one register.
+     * The old pair truth test treated RDX/RCX as a high word, but those
+     * registers are often just stale scratch values after loading a pointer.
+     * That made NULL pointers test true and let mem_init dereference 0.
+     */
     if (state->syntax & ASM_SYNTAX_INTEL) {
     
-        fprintf (state->ofp, "    test rdx, rdx\n");
-        fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jnz L%d\n" : "    jnz .L%d\n"), op == TOK_LOGOR ? true_label : end_label);
         fprintf (state->ofp, "    test rax, rax\n");
-        fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jnz L%d\n" : "    jnz .L%d\n"), op == TOK_LOGOR ? true_label : end_label);
         
-        if (op == TOK_LOGAND) {
+        if (op == TOK_LOGOR) {
         
-            fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jmp L%d\n" : "    jmp .L%d\n"), false_label);
-            fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), end_label);
-            fprintf (state->ofp, "    test rcx, rcx\n");
             fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jnz L%d\n" : "    jnz .L%d\n"), true_label);
             fprintf (state->ofp, "    test rbx, rbx\n");
             fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jnz L%d\n" : "    jnz .L%d\n"), true_label);
-            fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), false_label);
-            fprintf (state->ofp, "    xor rax, rax\n");
-            fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jmp L%d\n" : "    jmp .L%d\n"), end_label + 1);
-            fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), true_label);
-            fprintf (state->ofp, "    mov eax, 1\n");
-            fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), end_label + 1);
+            fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jmp L%d\n" : "    jmp .L%d\n"), false_label);
         
         } else {
         
-            fprintf (state->ofp, "    test rcx, rcx\n");
-            fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jnz L%d\n" : "    jnz .L%d\n"), true_label);
+            fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jz L%d\n" : "    jz .L%d\n"), false_label);
+            fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jmp L%d\n" : "    jmp .L%d\n"), rhs_label);
+            fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), rhs_label);
             fprintf (state->ofp, "    test rbx, rbx\n");
-            fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jnz L%d\n" : "    jnz .L%d\n"), true_label);
-            fprintf (state->ofp, "    xor rax, rax\n");
-            fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jmp L%d\n" : "    jmp .L%d\n"), end_label);
-            fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), true_label);
-            fprintf (state->ofp, "    mov eax, 1\n");
-            fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), end_label);
+            fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jz L%d\n" : "    jz .L%d\n"), false_label);
+            fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jmp L%d\n" : "    jmp .L%d\n"), true_label);
         
         }
+        
+        fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), false_label);
+        fprintf (state->ofp, "    xor rax, rax\n");
+        fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jmp L%d\n" : "    jmp .L%d\n"), end_label);
+        fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), true_label);
+        fprintf (state->ofp, "    mov eax, 1\n");
+        fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), end_label);
     
     } else {
     
-        fprintf (state->ofp, "    testq %%rdx, %%rdx\n");
-        fprintf (state->ofp, op == TOK_LOGOR ? "    jnz .L%d\n" : "    jnz .L%d\n", op == TOK_LOGOR ? true_label : end_label);
         fprintf (state->ofp, "    testq %%rax, %%rax\n");
         fprintf (state->ofp, op == TOK_LOGOR ? "    jnz .L%d\n" : "    jnz .L%d\n", op == TOK_LOGOR ? true_label : end_label);
         
-        if (op == TOK_LOGAND) {
+        if (op == TOK_LOGOR) {
         
-            fprintf (state->ofp, "    jmp .L%d\n", false_label);
-            fprintf (state->ofp, ".L%d:\n", end_label);
-            fprintf (state->ofp, "    testq %%rcx, %%rcx\n");
             fprintf (state->ofp, "    jnz .L%d\n", true_label);
             fprintf (state->ofp, "    testq %%rbx, %%rbx\n");
             fprintf (state->ofp, "    jnz .L%d\n", true_label);
-            fprintf (state->ofp, ".L%d:\n", false_label);
-            fprintf (state->ofp, "    xorq %%rax, %%rax\n");
-            fprintf (state->ofp, "    jmp .L%d\n", end_label + 1);
-            fprintf (state->ofp, ".L%d:\n", true_label);
-            fprintf (state->ofp, "    movl $1, %%eax\n");
-            fprintf (state->ofp, ".L%d:\n", end_label + 1);
+            fprintf (state->ofp, "    jmp .L%d\n", false_label);
         
         } else {
         
-            fprintf (state->ofp, "    testq %%rcx, %%rcx\n");
-            fprintf (state->ofp, "    jnz .L%d\n", true_label);
+            fprintf (state->ofp, "    jz .L%d\n", false_label);
+            fprintf (state->ofp, "    jmp .L%d\n", rhs_label);
+            fprintf (state->ofp, ".L%d:\n", rhs_label);
             fprintf (state->ofp, "    testq %%rbx, %%rbx\n");
-            fprintf (state->ofp, "    jnz .L%d\n", true_label);
-            fprintf (state->ofp, "    xorq %%rax, %%rax\n");
-            fprintf (state->ofp, "    jmp .L%d\n", end_label);
-            fprintf (state->ofp, ".L%d:\n", true_label);
-            fprintf (state->ofp, "    movl $1, %%eax\n");
-            fprintf (state->ofp, ".L%d:\n", end_label);
+            fprintf (state->ofp, "    jz .L%d\n", false_label);
+            fprintf (state->ofp, "    jmp .L%d\n", true_label);
         
         }
+        
+        fprintf (state->ofp, ".L%d:\n", false_label);
+        fprintf (state->ofp, "    xor %%rax, %%rax\n");
+        fprintf (state->ofp, "    jmp .L%d\n", end_label);
+        fprintf (state->ofp, ".L%d:\n", true_label);
+        fprintf (state->ofp, "    mov $1, %%eax\n");
+        fprintf (state->ofp, ".L%d:\n", end_label);
     
     }
     
@@ -33025,236 +33058,62 @@ static void emit_statement_cmp_rax_rdx_jump_if_false (enum token_kind op, int is
 
 static void emit_statement_cmp64_to_rax (enum token_kind op, int is_unsigned) {
 
-    int true_label;
-    int false_label;
-    int end_label;
+    const char *setcc;
     
     if (!state->ofp) {
         return;
     }
     
-    true_label = anon_label++;
-    false_label = anon_label++;
-    end_label = anon_label++;
-    
-    if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
+    /*
+     * AMD64 scalar integers and pointers are held in one 64-bit register.
+     * The old i386 long-long path treated a value as a high/low pair:
+     *
+     *     left  = rdx:rax
+     *     right = rcx:rbx
+     *
+     * but most AMD64 expression code only keeps the real value in RAX/RBX.
+     * RDX/RCX are scratch/stale.  Emitting a high-half compare here made
+     * expressions such as:
+     *
+     *     (size % 60) == 0
+     *
+     * depend on garbage in RCX/RDX, which broke kmalloc's size rounding and
+     * eventually made it return NULL.  Compare the actual 64-bit scalar
+     * registers only.
+     */
+    if (is_value_compare_operator (op)) {
     
-        switch (op) {
+        setcc = value_compare_set_mnemonic (op, is_unsigned);
         
-            case TOK_LESS:
-            
-                fprintf (state->ofp, "    cmp rdx, rcx\n");
-                fprintf (state->ofp, is_unsigned ? "    jb L%d\n" : "    jl L%d\n", true_label);
-                fprintf (state->ofp, is_unsigned ? "    ja L%d\n" : "    jg L%d\n", false_label);
-                fprintf (state->ofp, "    cmp rax, rbx\n");
-                fprintf (state->ofp, "    jb L%d\n", true_label);
-                break;
-            
-            case TOK_LTEQ:
-            
-                fprintf (state->ofp, "    cmp rdx, rcx\n");
-                fprintf (state->ofp, is_unsigned ? "    jb L%d\n" : "    jl L%d\n", true_label);
-                fprintf (state->ofp, is_unsigned ? "    ja L%d\n" : "    jg L%d\n", false_label);
-                fprintf (state->ofp, "    cmp rax, rbx\n");
-                fprintf (state->ofp, "    jbe L%d\n", true_label);
-                break;
-            
-            case TOK_GREATER:
-            
-                fprintf (state->ofp, "    cmp rdx, rcx\n");
-                fprintf (state->ofp, is_unsigned ? "    ja L%d\n" : "    jg L%d\n", true_label);
-                fprintf (state->ofp, is_unsigned ? "    jb L%d\n" : "    jl L%d\n", false_label);
-                fprintf (state->ofp, "    cmp rax, rbx\n");
-                fprintf (state->ofp, "    ja L%d\n", true_label);
-                break;
-            
-            case TOK_GTEQ:
-            
-                fprintf (state->ofp, "    cmp rdx, rcx\n");
-                fprintf (state->ofp, is_unsigned ? "    ja L%d\n" : "    jg L%d\n", true_label);
-                fprintf (state->ofp, is_unsigned ? "    jb L%d\n" : "    jl L%d\n", false_label);
-                fprintf (state->ofp, "    cmp rax, rbx\n");
-                fprintf (state->ofp, "    jae L%d\n", true_label);
-                break;
-            
-            case TOK_EQEQ:
-            
-                fprintf (state->ofp, "    cmp rdx, rcx\n");
-                fprintf (state->ofp, "    jne L%d\n", false_label);
-                fprintf (state->ofp, "    cmp rax, rbx\n");
-                fprintf (state->ofp, "    je L%d\n", true_label);
-                break;
-            
-            case TOK_NOTEQ:
-            
-                fprintf (state->ofp, "    cmp rdx, rcx\n");
-                fprintf (state->ofp, "    jne L%d\n", true_label);
-                fprintf (state->ofp, "    cmp rax, rbx\n");
-                fprintf (state->ofp, "    jne L%d\n", true_label);
-                break;
-            
-            default:
-            
-                fprintf (state->ofp, "    test rdx, rdx\n");
-                fprintf (state->ofp, "    jnz L%d\n", true_label);
-                fprintf (state->ofp, "    test rax, rax\n");
-                fprintf (state->ofp, "    jnz L%d\n", true_label);
-                break;
+        if (state->syntax & ASM_SYNTAX_INTEL) {
         
-        }
+            fprintf (state->ofp, "    cmp rax, rbx\n");
+            fprintf (state->ofp, "    %s al\n", setcc);
+            fprintf (state->ofp, "    movzx eax, al\n");
         
-        fprintf (state->ofp, "L%d:\n", false_label);
-        fprintf (state->ofp, "    xor rax, rax\n");
-        fprintf (state->ofp, "    jmp L%d\n", end_label);
-        fprintf (state->ofp, "L%d:\n", true_label);
-        fprintf (state->ofp, "    mov eax, 1\n");
-        fprintf (state->ofp, "L%d:\n", end_label);
-    
-    } else if (state->syntax & ASM_SYNTAX_INTEL) {
-    
-        switch (op) {
+        } else {
         
-            case TOK_LESS:
-            
-                fprintf (state->ofp, "    cmp rdx, rcx\n");
-                fprintf (state->ofp, is_unsigned ? "    jb .L%d\n" : "    jl .L%d\n", true_label);
-                fprintf (state->ofp, is_unsigned ? "    ja .L%d\n" : "    jg .L%d\n", false_label);
-                fprintf (state->ofp, "    cmp rax, rbx\n");
-                fprintf (state->ofp, "    jb .L%d\n", true_label);
-                break;
-            
-            case TOK_LTEQ:
-            
-                fprintf (state->ofp, "    cmp rdx, rcx\n");
-                fprintf (state->ofp, is_unsigned ? "    jb .L%d\n" : "    jl .L%d\n", true_label);
-                fprintf (state->ofp, is_unsigned ? "    ja .L%d\n" : "    jg .L%d\n", false_label);
-                fprintf (state->ofp, "    cmp rax, rbx\n");
-                fprintf (state->ofp, "    jbe .L%d\n", true_label);
-                break;
-            
-            case TOK_GREATER:
-            
-                fprintf (state->ofp, "    cmp rdx, rcx\n");
-                fprintf (state->ofp, is_unsigned ? "    ja .L%d\n" : "    jg .L%d\n", true_label);
-                fprintf (state->ofp, is_unsigned ? "    jb .L%d\n" : "    jl .L%d\n", false_label);
-                fprintf (state->ofp, "    cmp rax, rbx\n");
-                fprintf (state->ofp, "    ja .L%d\n", true_label);
-                break;
-            
-            case TOK_GTEQ:
-            
-                fprintf (state->ofp, "    cmp rdx, rcx\n");
-                fprintf (state->ofp, is_unsigned ? "    ja .L%d\n" : "    jg .L%d\n", true_label);
-                fprintf (state->ofp, is_unsigned ? "    jb .L%d\n" : "    jl .L%d\n", false_label);
-                fprintf (state->ofp, "    cmp rax, rbx\n");
-                fprintf (state->ofp, "    jae .L%d\n", true_label);
-                break;
-            
-            case TOK_EQEQ:
-            
-                fprintf (state->ofp, "    cmp rdx, rcx\n");
-                fprintf (state->ofp, "    jne .L%d\n", false_label);
-                fprintf (state->ofp, "    cmp rax, rbx\n");
-                fprintf (state->ofp, "    je .L%d\n", true_label);
-                break;
-            
-            case TOK_NOTEQ:
-            
-                fprintf (state->ofp, "    cmp rdx, rcx\n");
-                fprintf (state->ofp, "    jne .L%d\n", true_label);
-                fprintf (state->ofp, "    cmp rax, rbx\n");
-                fprintf (state->ofp, "    jne .L%d\n", true_label);
-                break;
-            
-            default:
-            
-                fprintf (state->ofp, "    test rdx, rdx\n");
-                fprintf (state->ofp, "    jnz .L%d\n", true_label);
-                fprintf (state->ofp, "    test rax, rax\n");
-                fprintf (state->ofp, "    jnz .L%d\n", true_label);
-                break;
+            fprintf (state->ofp, "    cmpq %%rbx, %%rax\n");
+            fprintf (state->ofp, "    %s %%al\n", setcc);
+            fprintf (state->ofp, "    movzbl %%al, %%eax\n");
         
         }
         
-        fprintf (state->ofp, ".L%d:\n", false_label);
-        fprintf (state->ofp, "    xor rax, rax\n");
-        fprintf (state->ofp, "    jmp .L%d\n", end_label);
-        fprintf (state->ofp, ".L%d:\n", true_label);
-        fprintf (state->ofp, "    mov eax, 1\n");
-        fprintf (state->ofp, ".L%d:\n", end_label);
+        return;
+    
+    }
+    
+    if (state->syntax & ASM_SYNTAX_INTEL) {
+    
+        fprintf (state->ofp, "    test rax, rax\n");
+        fprintf (state->ofp, "    setne al\n");
+        fprintf (state->ofp, "    movzx eax, al\n");
     
     } else {
     
-        switch (op) {
-        
-            case TOK_LESS:
-            
-                fprintf (state->ofp, "    cmpq %%rcx, %%rdx\n");
-                fprintf (state->ofp, is_unsigned ? "    jb .L%d\n" : "    jl .L%d\n", true_label);
-                fprintf (state->ofp, is_unsigned ? "    ja .L%d\n" : "    jg .L%d\n", false_label);
-                fprintf (state->ofp, "    cmpq %%rbx, %%rax\n");
-                fprintf (state->ofp, "    jb .L%d\n", true_label);
-                break;
-            
-            case TOK_LTEQ:
-            
-                fprintf (state->ofp, "    cmpq %%rcx, %%rdx\n");
-                fprintf (state->ofp, is_unsigned ? "    jb .L%d\n" : "    jl .L%d\n", true_label);
-                fprintf (state->ofp, is_unsigned ? "    ja .L%d\n" : "    jg .L%d\n", false_label);
-                fprintf (state->ofp, "    cmpq %%rbx, %%rax\n");
-                fprintf (state->ofp, "    jbe .L%d\n", true_label);
-                break;
-            
-            case TOK_GREATER:
-            
-                fprintf (state->ofp, "    cmpq %%rcx, %%rdx\n");
-                fprintf (state->ofp, is_unsigned ? "    ja .L%d\n" : "    jg .L%d\n", true_label);
-                fprintf (state->ofp, is_unsigned ? "    jb .L%d\n" : "    jl .L%d\n", false_label);
-                fprintf (state->ofp, "    cmpq %%rbx, %%rax\n");
-                fprintf (state->ofp, "    ja .L%d\n", true_label);
-                break;
-            
-            case TOK_GTEQ:
-            
-                fprintf (state->ofp, "    cmpq %%rcx, %%rdx\n");
-                fprintf (state->ofp, is_unsigned ? "    ja .L%d\n" : "    jg .L%d\n", true_label);
-                fprintf (state->ofp, is_unsigned ? "    jb .L%d\n" : "    jl .L%d\n", false_label);
-                fprintf (state->ofp, "    cmpq %%rbx, %%rax\n");
-                fprintf (state->ofp, "    jae .L%d\n", true_label);
-                break;
-            
-            case TOK_EQEQ:
-            
-                fprintf (state->ofp, "    cmpq %%rcx, %%rdx\n");
-                fprintf (state->ofp, "    jne .L%d\n", false_label);
-                fprintf (state->ofp, "    cmpq %%rbx, %%rax\n");
-                fprintf (state->ofp, "    je .L%d\n", true_label);
-                break;
-            
-            case TOK_NOTEQ:
-            
-                fprintf (state->ofp, "    cmpq %%rcx, %%rdx\n");
-                fprintf (state->ofp, "    jne .L%d\n", true_label);
-                fprintf (state->ofp, "    cmpq %%rbx, %%rax\n");
-                fprintf (state->ofp, "    jne .L%d\n", true_label);
-                break;
-            
-            default:
-            
-                fprintf (state->ofp, "    testq %%rdx, %%rdx\n");
-                fprintf (state->ofp, "    jnz .L%d\n", true_label);
-                fprintf (state->ofp, "    testq %%rax, %%rax\n");
-                fprintf (state->ofp, "    jnz .L%d\n", true_label);
-                break;
-        
-        }
-        
-        fprintf (state->ofp, ".L%d:\n", false_label);
-        fprintf (state->ofp, "    xorq %%rax, %%rax\n");
-        fprintf (state->ofp, "    jmp .L%d\n", end_label);
-        fprintf (state->ofp, ".L%d:\n", true_label);
-        fprintf (state->ofp, "    movl $1, %%eax\n");
-        fprintf (state->ofp, ".L%d:\n", end_label);
+        fprintf (state->ofp, "    testq %%rax, %%rax\n");
+        fprintf (state->ofp, "    setne %%al\n");
+        fprintf (state->ofp, "    movzbl %%al, %%eax\n");
     
     }
 
@@ -33392,29 +33251,21 @@ static void emit_statement_test_rax_jump_if_false (int label) {
 
 static void emit_statement_test_pair_jump_if_false (const char *lo, const char *hi, int label) {
 
-    int nonzero_label;
+    (void) hi;
     
     if (!state->ofp) {
         return;
     }
     
-    nonzero_label = anon_label++;
-    
     if (state->syntax & ASM_SYNTAX_INTEL) {
     
-        fprintf (state->ofp, "    test %s, %s\n", hi, hi);
-        fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jnz L%d\n" : "    jnz .L%d\n"), nonzero_label);
         fprintf (state->ofp, "    test %s, %s\n", lo, lo);
         fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jz L%d\n" : "    jz .L%d\n"), label);
-        fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "L%d:\n" : ".L%d:\n"), nonzero_label);
     
     } else {
     
-        fprintf (state->ofp, "    testq %%%s, %%%s\n", hi, hi);
-        fprintf (state->ofp, "    jnz .L%d\n", nonzero_label);
         fprintf (state->ofp, "    testq %%%s, %%%s\n", lo, lo);
         fprintf (state->ofp, "    jz .L%d\n", label);
-        fprintf (state->ofp, ".L%d:\n", nonzero_label);
     
     }
 
@@ -33422,21 +33273,19 @@ static void emit_statement_test_pair_jump_if_false (const char *lo, const char *
 
 static void emit_statement_test_pair_jump_if_true (const char *lo, const char *hi, int label) {
 
+    (void) hi;
+    
     if (!state->ofp) {
         return;
     }
     
     if (state->syntax & ASM_SYNTAX_INTEL) {
     
-        fprintf (state->ofp, "    test %s, %s\n", hi, hi);
-        fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jnz L%d\n" : "    jnz .L%d\n"), label);
         fprintf (state->ofp, "    test %s, %s\n", lo, lo);
         fprintf (state->ofp, (((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) ? "    jnz L%d\n" : "    jnz .L%d\n"), label);
     
     } else {
     
-        fprintf (state->ofp, "    testq %%%s, %%%s\n", hi, hi);
-        fprintf (state->ofp, "    jnz .L%d\n", label);
         fprintf (state->ofp, "    testq %%%s, %%%s\n", lo, lo);
         fprintf (state->ofp, "    jnz .L%d\n", label);
     
@@ -37029,8 +36878,8 @@ static int parse_possible_knr_function (void) {
                 char *param_name = 0;
                 
                 parse_type_spec ();
-                param_base_size = parsed_type_size;
                 
+                param_base_size = parsed_type_size;
                 preserve_pending_params++;
                 
                 declarator_is_pointer = 0;
@@ -38038,15 +37887,94 @@ static void emit_global_object (const char *name, int size, int is_array, long a
 
 }
 
+static void clear_pending_string_literals (void) {
+
+    int i;
+    
+    for (i = 0; i < pending_string_literal_count; i++) {
+    
+        if (pending_string_literals[i].label) {
+        
+            free (pending_string_literals[i].label);
+            pending_string_literals[i].label = 0;
+        
+        }
+        
+        if (pending_string_literals[i].values) {
+        
+            free (pending_string_literals[i].values);
+            pending_string_literals[i].values = 0;
+        
+        }
+        
+        pending_string_literals[i].elem_size = 0;
+        pending_string_literals[i].value_count = 0;
+    
+    }
+    
+    pending_string_literal_count = 0;
+
+}
+
+static void queue_string_literal_global (const char *label, const int64_s *values, int value_count, int elem_size) {
+
+    struct pending_string_literal *p;
+    int i;
+    
+    if (!label || !values || value_count <= 0 || !state->ofp) {
+        return;
+    }
+    
+    if (pending_string_literal_count >= MAX_PENDING_STRING_LITERALS) {
+    
+        report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.start, tok.caret, "too many string literals");
+        return;
+    
+    }
+    
+    p = &pending_string_literals[pending_string_literal_count++];
+    p->label = xstrdup (label);
+    p->elem_size = elem_size;
+    p->value_count = value_count;
+    p->values = xmalloc ((size_t) value_count * sizeof (*p->values));
+    
+    for (i = 0; i < value_count; i++) {
+        p->values[i] = values[i];
+    }
+
+}
+
+static void emit_pending_string_literals (void) {
+
+    int i, j;
+    
+    if (!state->ofp || pending_string_literal_count <= 0) {
+        return;
+    }
+    
+    switch_section (SECTION_DATA);
+    
+    for (i = 0; i < pending_string_literal_count; i++) {
+    
+        emit_global_data_label (pending_string_literals[i].label, 1);
+        
+        for (j = 0; j < pending_string_literals[i].value_count; j++) {
+            emit_global_scalar (pending_string_literals[i].values[j], pending_string_literals[i].elem_size);
+        }
+    
+    }
+    
+    clear_pending_string_literals ();
+
+}
+
 static char *emit_string_literal_global (void) {
 
     int64_s values[MAX_AGG_FIELDS];
-    
     char label[64];
-    char skip_label[64];
     
     int elem_size = DATA_CHAR & 0x1f;
-    int value_count = 0, i;
+    int value_count = 0;
     
     if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
         sprintf (label, "LC%d", anon_label++);
@@ -38069,37 +37997,7 @@ static char *emit_string_literal_global (void) {
         return xstrdup (label);
     }
     
-    if (current_section == SECTION_TEXT) {
-    
-        if ((state->syntax & ASM_SYNTAX_MASM) || (state->syntax & ASM_SYNTAX_NASM)) {
-            sprintf (skip_label, "L%d", anon_label++);
-        } else {
-            sprintf (skip_label, ".L%d", anon_label++);
-        }
-        
-        fprintf (state->ofp, "    jmp %s\n", skip_label);
-        
-        switch_section (SECTION_DATA);
-        emit_global_data_label (label, 1);
-        
-        for (i = 0; i < value_count; i++) {
-            emit_global_scalar (values[i], elem_size);
-        }
-        
-        switch_section (SECTION_TEXT);
-        emit_global_label (skip_label, 1);
-        
-        return xstrdup (label);
-    
-    }
-    
-    switch_section (SECTION_DATA);
-    emit_global_data_label (label, 1);
-    
-    for (i = 0; i < value_count; i++) {
-        emit_global_scalar (values[i], elem_size);
-    }
-    
+    queue_string_literal_global (label, values, value_count, elem_size);
     return xstrdup (label);
 
 }
@@ -38551,7 +38449,7 @@ void compile_translation_unit64 (void) {
     if (state->ofp) {
     
         if (state->syntax & ASM_SYNTAX_MASM) {
-            fprintf (state->ofp, ".model flat\n");
+            /*fprintf (state->ofp, ".model flat\n");*/
         } else if (state->syntax & ASM_SYNTAX_NASM) {
             fprintf (state->ofp, "bits 64\n");
         } else if (state->syntax & ASM_SYNTAX_INTEL) {
@@ -38566,6 +38464,7 @@ void compile_translation_unit64 (void) {
     clear_inline_functions ();
     clear_enum_constants ();
     clear_typedef_names ();
+    clear_pending_string_literals ();
     
     get_token ();
     
@@ -38574,8 +38473,8 @@ void compile_translation_unit64 (void) {
         if (is_type_start (tok.kind)) {
         
             parse_type_spec ();
-            parse_external_after_type ();
             
+            parse_external_after_type ();
             continue;
         
         }
@@ -38589,6 +38488,8 @@ void compile_translation_unit64 (void) {
     
     }
     
+    emit_pending_string_literals ();
+    
     if (state->syntax & ASM_SYNTAX_MASM) {
         masm_flush_data_line ();
     }
diff --git a/parse.c b/parse.c
index 017890ed5a7c26e938c9364c1992e116b26bb563..e05e6dda1449d015698e0349ef4952369617185f 100644 (file)
--- a/parse.c
+++ b/parse.c
@@ -1042,7 +1042,7 @@ int find_member_info_ex_bounded (const char *name, int max_size, const char *own
         for (i = member_info_count - 1; i >= 0; i--) {
         
             if (member_infos[i].name && strcmp (member_infos[i].name, name) == 0
-                && member_infos[i].owner_size == max_size) {
+                && (member_infos[i].owner_size == max_size || member_infos[i].owner_size == -max_size)) {
             
                 if (best < 0 || member_infos[i].offset < member_infos[best].offset) {
                     best = i;
@@ -2324,6 +2324,8 @@ long align_up_long (long value, int align) {
 
 int type_alignment (int size) {
 
+    size &= 0x1f;
+    
     if (size <= 1) {
         return 1;
     }
@@ -2332,6 +2334,10 @@ int type_alignment (int size) {
         return 2;
     }
     
+    if (size >= DATA_PTR) {
+        return DATA_PTR;
+    }
+    
     return 4;
 
 }
@@ -2895,7 +2901,20 @@ void parse_type_spec (void) {
                 for (mi = member_info_start; mi < member_info_count; mi++) {
                 
                     if (member_infos[mi].owner_size == 0) {
-                        member_infos[mi].owner_size = aggregate_size;
+                    
+                        /*
+                         * Keep union members overlaid.
+                         *
+                         * The backend may re-layout aggregate member metadata for
+                         * target alignment.  That is valid for structs, but not for
+                         * unions: all direct union members must remain at offset 0.
+                         * Store a negative owner size for union member records so
+                         * size-only backend relayout code will not mistake the union
+                         * for a struct with the same total size.  Parser lookups that
+                         * need the size still treat +/-aggregate_size as matching.
+                         */
+                        member_infos[mi].owner_size = is_union ? -aggregate_size : aggregate_size;
+                    
                     }
                     
                     if (owner_name && member_infos[mi].owner_tag_name == 0) {