/* output a single import thunk */ static void output_import_thunk( const char *name, const char *table, int pos ) { output( "\n\t.align %d\n", get_alignment(4) ); output( "\t%s\n", func_declaration(name) ); output( "%s\n", asm_globl(name) ); output_cfi( ".cfi_startproc" ); switch(target_cpu) { case CPU_x86: if (!UsePIC) { output( "\tjmp *(%s+%d)\n", table, pos ); } else { output( "\tcall %s\n", asm_name("__wine_spec_get_pc_thunk_eax") ); output( "1:\tjmp *%s+%d-1b(%%eax)\n", table, pos ); } break; case CPU_x86_64: output( "\tjmpq *%s+%d(%%rip)\n", table, pos ); break; case CPU_ARM: output( "\tldr IP,1f\n"); output( "\tldr PC,[PC,IP]\n" ); output( "1:\t.long %s+%u-(1b+4)\n", table, pos ); break; case CPU_ARM64: output( "\tadr x9, 1f\n" ); output( "\tldur x9, [x9, #0]\n" ); if (pos & 0xf000) output( "\tadd x9, x9, #%u\n", pos & 0xf000 ); if (pos & 0x0f00) output( "\tadd x9, x9, #%u\n", pos & 0x0f00 ); if (pos & 0x00f0) output( "\tadd x9, x9, #%u\n", pos & 0x00f0 ); if (pos & 0x000f) output( "\tadd x9, x9, #%u\n", pos & 0x000f ); output( "\tldur x9, [x9, #0]\n" ); output( "\tbr x9\n" ); output( "1:\t.quad %s\n", table ); break; case CPU_POWERPC: output( "\tmr %s, %s\n", ppc_reg(0), ppc_reg(31) ); if (target_platform == PLATFORM_APPLE) { output( "\tlis %s, ha16(%s+%d+32768)\n", ppc_reg(31), table, pos ); output( "\tla %s, lo16(%s+%d)(%s)\n", ppc_reg(31), table, pos, ppc_reg(31) ); } else { output( "\tlis %s, (%s+%d+32768)@h\n", ppc_reg(31), table, pos ); output( "\tla %s, (%s+%d)@l(%s)\n", ppc_reg(31), table, pos, ppc_reg(31) ); } output( "\tlwz %s, 0(%s)\n", ppc_reg(31), ppc_reg(31) ); output( "\tmtctr %s\n", ppc_reg(31) ); output( "\tmr %s, %s\n", ppc_reg(31), ppc_reg(0) ); output( "\tbctr\n" ); break; } output_cfi( ".cfi_endproc" ); output_function_size( name ); }
/* output the get_pc thunk if needed */ void output_get_pc_thunk(void) { if (target_cpu != CPU_x86) return; if (!UsePIC) return; output( "\n\t.text\n" ); output( "\t.align %d\n", get_alignment(4) ); output( "\t%s\n", func_declaration("__wine_spec_get_pc_thunk_eax") ); output( "%s:\n", asm_name("__wine_spec_get_pc_thunk_eax") ); output_cfi( ".cfi_startproc" ); output( "\tmovl (%%esp),%%eax\n" ); output( "\tret\n" ); output_cfi( ".cfi_endproc" ); output_function_size( "__wine_spec_get_pc_thunk_eax" ); }
/* output the delayed import thunks of a Win32 module */ static void output_delayed_import_thunks( const DLLSPEC *spec ) { int i, idx, j, pos, extra_stack_storage = 0; static const char delayed_import_loaders[] = "__wine_spec_delayed_import_loaders"; static const char delayed_import_thunks[] = "__wine_spec_delayed_import_thunks"; if (!nb_delayed) return; output( "\n/* delayed import thunks */\n\n" ); output( "\t.text\n" ); output( "\t.align %d\n", get_alignment(8) ); output( "%s:\n", asm_name(delayed_import_loaders)); output( "\t%s\n", func_declaration("__wine_delay_load_asm") ); output( "%s:\n", asm_name("__wine_delay_load_asm") ); output_cfi( ".cfi_startproc" ); switch(target_cpu) { case CPU_x86: output( "\tpushl %%ecx\n" ); output_cfi( ".cfi_adjust_cfa_offset 4" ); output( "\tpushl %%edx\n" ); output_cfi( ".cfi_adjust_cfa_offset 4" ); output( "\tpushl %%eax\n" ); output_cfi( ".cfi_adjust_cfa_offset 4" ); output( "\tcall %s\n", asm_name("__wine_spec_delay_load") ); output_cfi( ".cfi_adjust_cfa_offset -4" ); output( "\tpopl %%edx\n" ); output_cfi( ".cfi_adjust_cfa_offset -4" ); output( "\tpopl %%ecx\n" ); output_cfi( ".cfi_adjust_cfa_offset -4" ); output( "\tjmp *%%eax\n" ); break; case CPU_x86_64: output( "\tsubq $88,%%rsp\n" ); output_cfi( ".cfi_adjust_cfa_offset 88" ); output( "\tmovq %%rdx,80(%%rsp)\n" ); output( "\tmovq %%rcx,72(%%rsp)\n" ); output( "\tmovq %%r8,64(%%rsp)\n" ); output( "\tmovq %%r9,56(%%rsp)\n" ); output( "\tmovq %%r10,48(%%rsp)\n" ); output( "\tmovq %%r11,40(%%rsp)\n" ); output( "\tmovq %%rax,%%rcx\n" ); output( "\tcall %s\n", asm_name("__wine_spec_delay_load") ); output( "\tmovq 40(%%rsp),%%r11\n" ); output( "\tmovq 48(%%rsp),%%r10\n" ); output( "\tmovq 56(%%rsp),%%r9\n" ); output( "\tmovq 64(%%rsp),%%r8\n" ); output( "\tmovq 72(%%rsp),%%rcx\n" ); output( "\tmovq 80(%%rsp),%%rdx\n" ); output( "\taddq $88,%%rsp\n" ); output_cfi( ".cfi_adjust_cfa_offset -88" ); output( "\tjmp *%%rax\n" ); break; case CPU_SPARC: output( "\tsave %%sp, -96, %%sp\n" ); output( "\tcall %s\n", asm_name("__wine_spec_delay_load") ); output( "\tmov %%g1, %%o0\n" ); output( "\tjmp %%o0\n" ); output( "\trestore\n" ); break; case CPU_ARM: output( "\tstmfd SP!, {r4-r10,FP,LR}\n" ); output( "\tmov LR,PC\n"); output( "\tadd LR,LR,#8\n"); output( "\tldr PC,[PC,#-4]\n"); output( "\t.long %s\n", asm_name("__wine_spec_delay_load") ); output( "\tmov IP,r0\n"); output( "\tldmfd SP!, {r4-r10,FP,LR}\n" ); output( "\tldmfd SP!, {r0-r3}\n" ); output( "\tmov PC,IP\n"); break; case CPU_ARM64: output( "\tstp x29, x30, [sp,#-16]!\n" ); output( "\tmov x29, sp\n" ); output( "\tadr x9, 1f\n" ); output( "\tldur x9, [x9, #0]\n" ); output( "\tblr x9\n" ); output( "\tmov x9, x0\n" ); output( "\tldp x29, x30, [sp],#16\n" ); output( "\tldp x0, x1, [sp,#16]\n" ); output( "\tldp x2, x3, [sp,#32]\n" ); output( "\tldp x4, x5, [sp,#48]\n" ); output( "\tldp x6, x7, [sp],#80\n" ); output( "\tbr x9\n" ); /* or "ret x9" */ output( "1:\t.quad %s\n", asm_name("__wine_spec_delay_load") ); break; case CPU_POWERPC: if (target_platform == PLATFORM_APPLE) extra_stack_storage = 56; /* Save all callee saved registers into a stackframe. */ output( "\tstwu %s, -%d(%s)\n",ppc_reg(1), 48+extra_stack_storage, ppc_reg(1)); output( "\tstw %s, %d(%s)\n", ppc_reg(3), 4+extra_stack_storage, ppc_reg(1)); output( "\tstw %s, %d(%s)\n", ppc_reg(4), 8+extra_stack_storage, ppc_reg(1)); output( "\tstw %s, %d(%s)\n", ppc_reg(5), 12+extra_stack_storage, ppc_reg(1)); output( "\tstw %s, %d(%s)\n", ppc_reg(6), 16+extra_stack_storage, ppc_reg(1)); output( "\tstw %s, %d(%s)\n", ppc_reg(7), 20+extra_stack_storage, ppc_reg(1)); output( "\tstw %s, %d(%s)\n", ppc_reg(8), 24+extra_stack_storage, ppc_reg(1)); output( "\tstw %s, %d(%s)\n", ppc_reg(9), 28+extra_stack_storage, ppc_reg(1)); output( "\tstw %s, %d(%s)\n", ppc_reg(10),32+extra_stack_storage, ppc_reg(1)); output( "\tstw %s, %d(%s)\n", ppc_reg(11),36+extra_stack_storage, ppc_reg(1)); output( "\tstw %s, %d(%s)\n", ppc_reg(12),40+extra_stack_storage, ppc_reg(1)); /* r0 -> r3 (arg1) */ output( "\tmr %s, %s\n", ppc_reg(3), ppc_reg(0)); /* save return address */ output( "\tmflr %s\n", ppc_reg(0)); output( "\tstw %s, %d(%s)\n", ppc_reg(0), 44+extra_stack_storage, ppc_reg(1)); /* Call the __wine_delay_load function, arg1 is arg1. */ output( "\tbl %s\n", asm_name("__wine_spec_delay_load") ); /* Load return value from call into ctr register */ output( "\tmtctr %s\n", ppc_reg(3)); /* restore all saved registers and drop stackframe. */ output( "\tlwz %s, %d(%s)\n", ppc_reg(3), 4+extra_stack_storage, ppc_reg(1)); output( "\tlwz %s, %d(%s)\n", ppc_reg(4), 8+extra_stack_storage, ppc_reg(1)); output( "\tlwz %s, %d(%s)\n", ppc_reg(5), 12+extra_stack_storage, ppc_reg(1)); output( "\tlwz %s, %d(%s)\n", ppc_reg(6), 16+extra_stack_storage, ppc_reg(1)); output( "\tlwz %s, %d(%s)\n", ppc_reg(7), 20+extra_stack_storage, ppc_reg(1)); output( "\tlwz %s, %d(%s)\n", ppc_reg(8), 24+extra_stack_storage, ppc_reg(1)); output( "\tlwz %s, %d(%s)\n", ppc_reg(9), 28+extra_stack_storage, ppc_reg(1)); output( "\tlwz %s, %d(%s)\n", ppc_reg(10),32+extra_stack_storage, ppc_reg(1)); output( "\tlwz %s, %d(%s)\n", ppc_reg(11),36+extra_stack_storage, ppc_reg(1)); output( "\tlwz %s, %d(%s)\n", ppc_reg(12),40+extra_stack_storage, ppc_reg(1)); /* Load return value from call into return register */ output( "\tlwz %s, %d(%s)\n", ppc_reg(0), 44+extra_stack_storage, ppc_reg(1)); output( "\tmtlr %s\n", ppc_reg(0)); output( "\taddi %s, %s, %d\n", ppc_reg(1), ppc_reg(1), 48+extra_stack_storage); /* branch to ctr register. */ output( "\tbctr\n"); break; } output_cfi( ".cfi_endproc" ); output_function_size( "__wine_delay_load_asm" ); output( "\n" ); for (i = idx = 0; i < nb_imports; i++) { if (!dll_imports[i]->delay) continue; for (j = 0; j < dll_imports[i]->nb_imports; j++) { ORDDEF *odp = dll_imports[i]->imports[j]; const char *name = odp->name ? odp->name : odp->export_name; output( ".L__wine_delay_imp_%d_%s:\n", i, name ); output_cfi( ".cfi_startproc" ); switch(target_cpu) { case CPU_x86: output( "\tmovl $%d, %%eax\n", (idx << 16) | j ); output( "\tjmp %s\n", asm_name("__wine_delay_load_asm") ); break; case CPU_x86_64: output( "\tmovq $%d,%%rax\n", (idx << 16) | j ); output( "\tjmp %s\n", asm_name("__wine_delay_load_asm") ); break; case CPU_SPARC: output( "\tset %d, %%g1\n", (idx << 16) | j ); output( "\tb,a %s\n", asm_name("__wine_delay_load_asm") ); output( "\tnop\n" ); break; case CPU_ARM: output( "\tstmfd SP!, {r0-r3}\n" ); output( "\tmov r0, #%d\n", idx ); output( "\tmov r1, #16384\n" ); output( "\tmul r1, r0, r1\n" ); output( "\tmov r0, r1\n" ); output( "\tmov r1, #4\n" ); output( "\tmul r1, r0, r1\n" ); output( "\tmov r0, r1\n" ); output( "\tadd r0, #%d\n", j ); output( "\tldr PC,[PC,#-4]\n"); output( "\t.long %s\n", asm_name("__wine_delay_load_asm") ); break; case CPU_ARM64: output( "\tstp x6, x7, [sp,#-80]!\n" ); output( "\tstp x4, x5, [sp,#48]\n" ); output( "\tstp x2, x3, [sp,#32]\n" ); output( "\tstp x0, x1, [sp,#16]\n" ); output( "\tmov x0, #%d\n", idx ); output( "\tmov x1, #16384\n" ); output( "\tmul x1, x0, x1\n" ); output( "\tmov x0, x1\n" ); output( "\tmov x1, #4\n" ); output( "\tmul x1, x0, x1\n" ); output( "\tmov x0, x1\n" ); output( "\tadd x0, x0, #%d\n", j ); output( "\tadr x9, 1f\n" ); output( "\tldur x9, [x9, #0]\n" ); output( "\tbr x9\n" ); output( "1:\t.quad %s\n", asm_name("__wine_delay_load_asm") ); break; case CPU_POWERPC: switch(target_platform) { case PLATFORM_APPLE: /* On Darwin we can use r0 and r2 */ /* Upper part in r2 */ output( "\tlis %s, %d\n", ppc_reg(2), idx); /* Lower part + r2 -> r0, Note we can't use r0 directly */ output( "\taddi %s, %s, %d\n", ppc_reg(0), ppc_reg(2), j); output( "\tb %s\n", asm_name("__wine_delay_load_asm") ); break; default: /* On linux we can't use r2 since r2 is not a scratch register (hold the TOC) */ /* Save r13 on the stack */ output( "\taddi %s, %s, -0x4\n", ppc_reg(1), ppc_reg(1)); output( "\tstw %s, 0(%s)\n", ppc_reg(13), ppc_reg(1)); /* Upper part in r13 */ output( "\tlis %s, %d\n", ppc_reg(13), idx); /* Lower part + r13 -> r0, Note we can't use r0 directly */ output( "\taddi %s, %s, %d\n", ppc_reg(0), ppc_reg(13), j); /* Restore r13 */ output( "\tstw %s, 0(%s)\n", ppc_reg(13), ppc_reg(1)); output( "\taddic %s, %s, 0x4\n", ppc_reg(1), ppc_reg(1)); output( "\tb %s\n", asm_name("__wine_delay_load_asm") ); break; } break; } output_cfi( ".cfi_endproc" ); } idx++; } output_function_size( delayed_import_loaders ); output( "\n\t.align %d\n", get_alignment(get_ptr_size()) ); output( "%s:\n", asm_name(delayed_import_thunks)); for (i = pos = 0; i < nb_imports; i++) { if (!dll_imports[i]->delay) continue; for (j = 0; j < dll_imports[i]->nb_imports; j++, pos += get_ptr_size()) { ORDDEF *odp = dll_imports[i]->imports[j]; output_import_thunk( odp->name ? odp->name : odp->export_name, ".L__wine_delay_IAT", pos ); } } output_function_size( delayed_import_thunks ); }
/* output a single import thunk */ static void output_import_thunk( const char *name, const char *table, int pos ) { output( "\n\t.align %d\n", get_alignment(4) ); output( "\t%s\n", func_declaration(name) ); output( "%s\n", asm_globl(name) ); output_cfi( ".cfi_startproc" ); switch(target_cpu) { case CPU_x86: if (!UsePIC) { output( "\tjmp *(%s+%d)\n", table, pos ); } else { output( "\tcall %s\n", asm_name("__wine_spec_get_pc_thunk_eax") ); output( "1:\tjmp *%s+%d-1b(%%eax)\n", table, pos ); } break; case CPU_x86_64: output( "\tjmpq *%s+%d(%%rip)\n", table, pos ); break; case CPU_SPARC: if ( !UsePIC ) { output( "\tsethi %%hi(%s+%d), %%g1\n", table, pos ); output( "\tld [%%g1+%%lo(%s+%d)], %%g1\n", table, pos ); output( "\tjmp %%g1\n" ); output( "\tnop\n" ); } else { /* Hmpf. Stupid sparc assembler always interprets global variable names as GOT offsets, so we have to do it the long way ... */ output( "\tsave %%sp, -96, %%sp\n" ); output( "0:\tcall 1f\n" ); output( "\tnop\n" ); output( "1:\tsethi %%hi(%s+%d-0b), %%g1\n", table, pos ); output( "\tor %%g1, %%lo(%s+%d-0b), %%g1\n", table, pos ); output( "\tld [%%g1+%%o7], %%g1\n" ); output( "\tjmp %%g1\n" ); output( "\trestore\n" ); } break; case CPU_ARM: output( "\tldr IP,[PC,#0]\n"); output( "\tldr PC,[IP,#%d]\n", pos); output( "\t.long %s\n", table ); break; case CPU_ARM64: output( "\tadr x9, 1f\n" ); output( "\tldur x9, [x9, #0]\n" ); if (pos & 0xf000) output( "\tadd x9, x9, #%u\n", pos & 0xf000 ); if (pos & 0x0f00) output( "\tadd x9, x9, #%u\n", pos & 0x0f00 ); if (pos & 0x00f0) output( "\tadd x9, x9, #%u\n", pos & 0x00f0 ); if (pos & 0x000f) output( "\tadd x9, x9, #%u\n", pos & 0x000f ); output( "\tldur x9, [x9, #0]\n" ); output( "\tbr x9\n" ); output( "1:\t.quad %s\n", table ); break; case CPU_POWERPC: output( "\tmr %s, %s\n", ppc_reg(0), ppc_reg(31) ); if (target_platform == PLATFORM_APPLE) { output( "\tlis %s, ha16(%s+%d+32768)\n", ppc_reg(31), table, pos ); output( "\tla %s, lo16(%s+%d)(%s)\n", ppc_reg(31), table, pos, ppc_reg(31) ); } else { output( "\tlis %s, (%s+%d+32768)@h\n", ppc_reg(31), table, pos ); output( "\tla %s, (%s+%d)@l(%s)\n", ppc_reg(31), table, pos, ppc_reg(31) ); } output( "\tlwz %s, 0(%s)\n", ppc_reg(31), ppc_reg(31) ); output( "\tmtctr %s\n", ppc_reg(31) ); output( "\tmr %s, %s\n", ppc_reg(31), ppc_reg(0) ); output( "\tbctr\n" ); break; } output_cfi( ".cfi_endproc" ); output_function_size( name ); }
/******************************************************************* * output_stubs * * Output the functions for stub entry points */ void output_stubs( DLLSPEC *spec ) { const char *name, *exp_name; int i, count; if (!has_stubs( spec )) return; output( "\n/* stub functions */\n\n" ); output( "\t.text\n" ); for (i = count = 0; i < spec->nb_entry_points; i++) { ORDDEF *odp = &spec->entry_points[i]; if (odp->type != TYPE_STUB) continue; name = get_stub_name( odp, spec ); exp_name = odp->name ? odp->name : odp->export_name; output( "\t.align %d\n", get_alignment(4) ); output( "\t%s\n", func_declaration(name) ); output( "%s:\n", asm_name(name) ); output_cfi( ".cfi_startproc" ); switch (target_cpu) { case CPU_x86: /* flesh out the stub a bit to make safedisc happy */ output(" \tnop\n" ); output(" \tnop\n" ); output(" \tnop\n" ); output(" \tnop\n" ); output(" \tnop\n" ); output(" \tnop\n" ); output(" \tnop\n" ); output(" \tnop\n" ); output(" \tnop\n" ); output( "\tsubl $12,%%esp\n" ); output_cfi( ".cfi_adjust_cfa_offset 12" ); if (UsePIC) { output( "\tcall %s\n", asm_name("__wine_spec_get_pc_thunk_eax") ); output( "1:" ); if (exp_name) { output( "\tleal .L%s_string-1b(%%eax),%%ecx\n", name ); output( "\tmovl %%ecx,4(%%esp)\n" ); count++; } else output( "\tmovl $%d,4(%%esp)\n", odp->ordinal ); output( "\tleal .L__wine_spec_file_name-1b(%%eax),%%ecx\n" ); output( "\tmovl %%ecx,(%%esp)\n" ); } else { if (exp_name) { output( "\tmovl $.L%s_string,4(%%esp)\n", name ); count++; } else output( "\tmovl $%d,4(%%esp)\n", odp->ordinal ); output( "\tmovl $.L__wine_spec_file_name,(%%esp)\n" ); } output( "\tcall %s\n", asm_name("__wine_spec_unimplemented_stub") ); break; case CPU_x86_64: output( "\tsubq $8,%%rsp\n" ); output_cfi( ".cfi_adjust_cfa_offset 8" ); output( "\tleaq .L__wine_spec_file_name(%%rip),%%rdi\n" ); if (exp_name) { output( "leaq .L%s_string(%%rip),%%rsi\n", name ); count++; } else output( "\tmovq $%d,%%rsi\n", odp->ordinal ); output( "\tcall %s\n", asm_name("__wine_spec_unimplemented_stub") ); break; case CPU_ARM: output( "\tldr r0,[PC,#0]\n"); output( "\tmov PC,PC\n"); output( "\t.long .L__wine_spec_file_name\n" ); output( "\tldr r1,[PC,#0]\n"); output( "\tmov PC,PC\n"); if (exp_name) { output( "\t.long .L%s_string\n", name ); count++; } else output( "\t.long %d\n", odp->ordinal ); output( "\tbl %s\n", asm_name("__wine_spec_unimplemented_stub") ); break; default: assert(0); } output_cfi( ".cfi_endproc" ); output_function_size( name ); } if (count) { output( "\t%s\n", get_asm_string_section() ); for (i = 0; i < spec->nb_entry_points; i++) { ORDDEF *odp = &spec->entry_points[i]; if (odp->type != TYPE_STUB) continue; exp_name = odp->name ? odp->name : odp->export_name; if (exp_name) { name = get_stub_name( odp, spec ); output( ".L%s_string:\n", name ); output( "\t%s \"%s\"\n", get_asm_string_keyword(), exp_name ); } } } }
static void function_header( const char *name ) { output( "\n\t.align %d\n", get_alignment(4) ); output( "\t%s\n", func_declaration(name) ); output( "%s\n", asm_globl(name) ); }
/******************************************************************* * output_call16_function * * Build a 16-bit-to-Wine callback glue function. * * The generated routines are intended to be used as argument conversion * routines to be called by the CallFrom16... core. Thus, the prototypes of * the generated routines are (see also CallFrom16): * * extern WORD WINAPI __wine_spec_call16_C_xxx( FARPROC func, LPBYTE args ); * extern LONG WINAPI __wine_spec_call16_C_xxx( FARPROC func, LPBYTE args ); * extern void WINAPI __wine_spec_call16_C_xxx_regs( FARPROC func, LPBYTE args, CONTEXT86 *context ); * * where 'C' is the calling convention ('p' for pascal or 'c' for cdecl), * and each 'x' is an argument ('w'=word, 's'=signed word, 'l'=long, * 'p'=linear pointer, 't'=linear pointer to null-terminated string, * 'T'=segmented pointer to null-terminated string). * * The generated routines fetch the arguments from the 16-bit stack (pointed * to by 'args'); the offsets of the single argument values are computed * according to the calling convention and the argument types. Then, the * 32-bit entry point is called with these arguments. * * For register functions, the arguments (if present) are converted just * the same as for normal functions, but in addition the CONTEXT86 pointer * filled with the current register values is passed to the 32-bit routine. */ static void output_call16_function( ORDDEF *odp ) { char *name; int i, pos, stack_words; int argsize = get_function_argsize( odp ); int needs_ldt = (strpbrk( get_args_str( odp ), "pt" ) != NULL); name = strmake( ".L__wine_spec_call16_%s", get_relay_name(odp) ); output( "\t.align %d\n", get_alignment(4) ); output( "\t%s\n", func_declaration(name) ); output( "%s:\n", name ); output_cfi( ".cfi_startproc" ); output( "\tpushl %%ebp\n" ); output_cfi( ".cfi_adjust_cfa_offset 4" ); output_cfi( ".cfi_rel_offset %%ebp,0" ); output( "\tmovl %%esp,%%ebp\n" ); output_cfi( ".cfi_def_cfa_register %%ebp" ); stack_words = 2; if (needs_ldt) { output( "\tpushl %%esi\n" ); output_cfi( ".cfi_rel_offset %%esi,-4" ); stack_words++; if (UsePIC) { output( "\tcall %s\n", asm_name("__wine_spec_get_pc_thunk_eax") ); output( "1:\tmovl wine_ldt_copy_ptr-1b(%%eax),%%esi\n" ); } else output( "\tmovl $%s,%%esi\n", asm_name("_imp__wine_ldt_copy") ); } /* preserve 16-byte stack alignment */ stack_words += odp->u.func.nb_args; for (i = 0; i < odp->u.func.nb_args; i++) if (odp->u.func.args[i] == ARG_DOUBLE || odp->u.func.args[i] == ARG_INT64) stack_words++; if ((odp->flags & FLAG_REGISTER) || (odp->type == TYPE_VARARGS)) stack_words++; if (stack_words % 4) output( "\tsubl $%d,%%esp\n", 16 - 4 * (stack_words % 4) ); if (odp->u.func.nb_args || odp->type == TYPE_VARARGS) output( "\tmovl 12(%%ebp),%%ecx\n" ); /* args */ if (odp->flags & FLAG_REGISTER) { output( "\tpushl 16(%%ebp)\n" ); /* context */ } else if (odp->type == TYPE_VARARGS) { output( "\tleal %d(%%ecx),%%eax\n", argsize ); output( "\tpushl %%eax\n" ); /* va_list16 */ } pos = (odp->type == TYPE_PASCAL) ? 0 : argsize; for (i = odp->u.func.nb_args - 1; i >= 0; i--) { switch (odp->u.func.args[i]) { case ARG_WORD: if (odp->type != TYPE_PASCAL) pos -= 2; output( "\tmovzwl %d(%%ecx),%%eax\n", pos ); output( "\tpushl %%eax\n" ); if (odp->type == TYPE_PASCAL) pos += 2; break; case ARG_SWORD: if (odp->type != TYPE_PASCAL) pos -= 2; output( "\tmovswl %d(%%ecx),%%eax\n", pos ); output( "\tpushl %%eax\n" ); if (odp->type == TYPE_PASCAL) pos += 2; break; case ARG_INT64: case ARG_DOUBLE: if (odp->type != TYPE_PASCAL) pos -= 4; output( "\tpushl %d(%%ecx)\n", pos ); if (odp->type == TYPE_PASCAL) pos += 4; /* fall through */ case ARG_LONG: case ARG_FLOAT: case ARG_SEGPTR: case ARG_SEGSTR: if (odp->type != TYPE_PASCAL) pos -= 4; output( "\tpushl %d(%%ecx)\n", pos ); if (odp->type == TYPE_PASCAL) pos += 4; break; case ARG_PTR: case ARG_STR: case ARG_WSTR: case ARG_INT128: if (odp->type != TYPE_PASCAL) pos -= 4; output( "\tmovzwl %d(%%ecx),%%edx\n", pos + 2 ); /* sel */ output( "\tshr $3,%%edx\n" ); output( "\tmovzwl %d(%%ecx),%%eax\n", pos ); /* offset */ output( "\taddl (%%esi,%%edx,4),%%eax\n" ); output( "\tpushl %%eax\n" ); if (odp->type == TYPE_PASCAL) pos += 4; break; } } output( "\tcall *8(%%ebp)\n" ); if (needs_ldt) { output( "\tmovl -4(%%ebp),%%esi\n" ); output_cfi( ".cfi_same_value %%esi" ); } output( "\tleave\n" ); output_cfi( ".cfi_def_cfa %%esp,4" ); output_cfi( ".cfi_same_value %%ebp" ); output( "\tret\n" ); output_cfi( ".cfi_endproc" ); output_function_size( name ); free( name ); }
static int F() { // function declaration Tdata_type ftype; switch (token.type) { case TT_INT: case TT_DOUBLE: case TT_STRING: // rule: type idf ( AL ) FD ftype = get_type(); tmp_ftype = ftype; next(); check(TT_ID); try(add_func(ftype, token.p_string)); next(); term(TT_L_BRACKET); call(AL); term(TT_R_BRACKET); return(FD); default: return ERR_PARSER; } } static int AL() { // first function argument switch (token.type) { case TT_INT: case TT_DOUBLE: case TT_STRING: // rule: AL -> AD AL2 call(AD); return(AL2); default: // rule: AL -> eps return OK; } } static int AL2() { // remaining function arguments switch (token.type) { case TT_COMMA: // rule: AL2 -> , AD AL2 next(); call(AD); return(AL2); default: // rule: AL2 -> eps return OK; } } static int AD() { // argument Tdata_type atype; switch (token.type) { case TT_INT: case TT_DOUBLE: case TT_STRING: // rule: AD -> type id atype = get_type(); next(); check(TT_ID); try(add_param(atype, token.p_string)); next(); return OK; default: return ERR_PARSER; } } static int FD() { // function definition unsigned i; switch (token.type) { case TT_SEMICOLON: // rule: FD -> ; try(func_declaration()); next(); return OK; case TT_OPEN_BRACKET: // rule: FD -> { STL } i = get_line(); try(func_definition(i)); // instruction pointer next(); call(STL); term(TT_CLOSE_BRACKET); remove_block(); generate(I_ERR, NULL, NULL, NULL); // no return => runtime error return OK; default: return ERR_PARSER; } } static int STL() { // stat list switch (token.type) { case TT_CLOSE_BRACKET: // rule: STL -> eps return OK; default: // rule: STL -> S STL call(S); return(STL); } } static int S() { // stat unsigned i; Tlocal_sym_tab_node *conv; switch (token.type) { case TT_AUTO: case TT_INT: case TT_DOUBLE: case TT_STRING: // rule: S -> D ; call(D); term(TT_SEMICOLON); return OK; case TT_OPEN_BRACKET: // rule: S -> { STL } next(); try(add_block()); call(STL); term(TT_CLOSE_BRACKET); remove_block(); return OK; case TT_IF: // rule: S -> if ( E ) S else S next(); term(TT_L_BRACKET); call(E); try(type_compare(tmp_psitem.type, TYPE_INT)); if (tmp_psitem.type != TYPE_INT) { try(tmp_var(TYPE_INT, &conv)); generate(I_CONVERT, tmp_psitem.value.ptr, NULL, conv); } else { conv = tmp_psitem.value.ptr; } Tlocal_sym_tab_node *label; try(tmp_var(TYPE_INT, &label)); generate(I_JUMPIFN, conv, label, NULL); term(TT_R_BRACKET); call(S); Tlocal_sym_tab_node *endif; try(tmp_var(TYPE_INT, &endif)); generate(I_JUMP, NULL, endif, NULL); i = get_line(); set_var_value_int(label->name, i); call(ELSE); i = get_line(); set_var_value_int(endif->name, i); return OK; case TT_FOR: // rule: S -> for ( D ; E ; E ) S try(add_block()); next(); term(TT_L_BRACKET); call(D); term(TT_SEMICOLON); Tlocal_sym_tab_node *cond; try(tmp_var(TYPE_INT, &cond)); i = get_line(); set_var_value_int(cond->name, i); call(E); try(type_compare(tmp_psitem.type, TYPE_INT)); if (tmp_psitem.type != TYPE_INT) { try(tmp_var(TYPE_INT, &conv)); generate(I_CONVERT, tmp_psitem.value.ptr, NULL, conv); } else { conv = tmp_psitem.value.ptr; } Tlocal_sym_tab_node *endfor; try(tmp_var(TYPE_INT, &endfor)); generate(I_JUMPIFN, conv, endfor, NULL); Tlocal_sym_tab_node *statlist; try(tmp_var(TYPE_INT, &statlist)); generate(I_JUMP, NULL, statlist, NULL); Tlocal_sym_tab_node *assign; try(tmp_var(TYPE_INT, &assign)); i = get_line(); set_var_value_int(assign->name, i); term(TT_SEMICOLON); call(E); generate(I_JUMP, NULL, cond, NULL); i = get_line(); set_var_value_int(statlist->name, i); term(TT_R_BRACKET); call(S); generate(I_JUMP, NULL, assign, NULL); i = get_line(); set_var_value_int(endfor->name, i); remove_block(); return OK; case TT_WHILE: // rule: S -> while ( E ) S next(); term(TT_L_BRACKET); Tlocal_sym_tab_node *wcond; try(tmp_var(TYPE_INT, &wcond)); i = get_line(); set_var_value_int(wcond->name, i); call(E); try(type_compare(tmp_psitem.type, TYPE_INT)); if (tmp_psitem.type != TYPE_INT) { try(tmp_var(TYPE_INT, &conv)); generate(I_CONVERT, tmp_psitem.value.ptr, NULL, conv); } else { conv = tmp_psitem.value.ptr; } Tlocal_sym_tab_node *endwhile; try(tmp_var(TYPE_INT, &endwhile)); generate(I_JUMPIFN, conv, endwhile, NULL); term (TT_R_BRACKET); call(S); generate(I_JUMP, NULL, wcond, NULL); i = get_line(); set_var_value_int(endwhile->name, i); return OK; case TT_DO: // rule: S -> do S while ( E ) ; next(); Tlocal_sym_tab_node *dwbody; try(tmp_var(TYPE_INT, &dwbody)); i = get_line(); set_var_value_int(dwbody->name, i); call(S); term(TT_WHILE); term(TT_L_BRACKET); call(E); try(type_compare(tmp_psitem.type, TYPE_INT)); if (tmp_psitem.type != TYPE_INT) { try(tmp_var(TYPE_INT, &conv)); generate(I_CONVERT, tmp_psitem.value.ptr, NULL, conv); } else { conv = tmp_psitem.value.ptr; } generate(I_JUMPIF, conv, dwbody, NULL); term(TT_R_BRACKET); term(TT_SEMICOLON); return OK; case TT_RETURN: // rule: S -> return E ; next(); call(E); try(type_compare(tmp_ftype, tmp_psitem.type)); if (tmp_psitem.type != tmp_ftype) { try(tmp_var(tmp_ftype, &conv)); generate(I_CONVERT, tmp_psitem.value.ptr, NULL, conv); } else { conv = tmp_psitem.value.ptr; } generate(I_RETURN, conv, NULL, NULL); term(TT_SEMICOLON); return OK; case TT_CIN: // rule: S -> cin >> id IN ; next(); term(TT_IN); check(TT_ID); if (!var_exists(token.p_string)) return ERR_UNDEF; Tlocal_sym_tab_node *var = get_var(token.p_string); generate(I_CIN, NULL, NULL, var); next(); call(IN); term(TT_SEMICOLON); return OK; case TT_COUT: // rule: S -> cout << E OUT ; next(); term(TT_OUT); call(E); generate(I_COUT, tmp_psitem.value.ptr, NULL, NULL); call(OUT); term(TT_SEMICOLON); return OK; default: // rule: S -> E ; call(E); term(TT_SEMICOLON); return OK; } } static int D() { // variable declaration Tdata_type vtype; switch (token.type) { case TT_AUTO: case TT_INT: case TT_DOUBLE: case TT_STRING: // rule: D -> type id I vtype = get_type(); tmp_type = vtype; next(); check(TT_ID); try(add_var(vtype, token.p_string)); try(tmpstr(token.p_string)); next(); return(I); default: return ERR_PARSER; } } static int I() { // variable definition switch (token.type) { case TT_ASSIGN: // rule: I -> = E next(); call(E); try(assign_type_compare(tmp_buffer.str, tmp_psitem.type)); Tlocal_sym_tab_node *var = get_var(tmp_buffer.str); Tlocal_sym_tab_node *conv; if (tmp_psitem.type != var->data_type) { try(tmp_var(var->data_type, &conv)); generate(I_CONVERT, tmp_psitem.value.ptr, NULL, conv); } else { conv = tmp_psitem.value.ptr; } generate(I_ASSIGN, conv, NULL, var); return OK; default: // rule: I -> eps if (tmp_type == TYPE_AUTO) return ERR_AUTO; return OK; } } static int ELSE() { // else switch (token.type) { case TT_ELSE: // rule: ELSE -> else S next(); return(S); default: // rule: ELSE -> eps return OK; } } static int IN() { // input variables switch (token.type) { case TT_IN: // rule: IN -> >> id IN next(); check(TT_ID); if (!var_exists(token.p_string)) return ERR_UNDEF; Tlocal_sym_tab_node *var = get_var(token.p_string); generate(I_CIN, NULL, NULL, var); next(); return(IN); default: // rule: IN -> eps return OK; } } static int OUT() { // output expressions switch (token.type) { case TT_OUT: // rule: OUT -> << E OUT next(); call(E); generate(I_COUT, tmp_psitem.value.ptr, NULL, NULL); return(OUT); default: // rule: OUT -> eps return OK; } } static int E() { // expression Tpsa_stack *psa_stack = ps_init(); ps_push(psa_stack, OP_END); if (get_prec(OP_END) == ' ') { fprintf(stderr, "Syntax error: 'expression' expected, but '%s' given.\n", token_[token.type]); return ERR_PARSER; } int err; do { Tps_item *ps_item = ps_first_term(psa_stack); switch (get_prec(ps_item->op)) { case '=': err = ps_push_token(psa_stack); if (err != OK) { ps_destroy(psa_stack); return err; } next(); break; case '<': err = ps_ins_after(psa_stack, ps_item, OP_EXPR); if (err != OK) { ps_destroy(psa_stack); return err; } err = ps_push_token(psa_stack); if (err != OK) { ps_destroy(psa_stack); return err; } next(); break; case '>': err = psa(psa_stack); if (err != OK) { ps_destroy(psa_stack); return err; } break; } } while (get_prec(ps_first_term(psa_stack)->op) != ' '); Tps_item *top = ps_top(psa_stack); if (top->op != OP_NTERM || ps_prev(psa_stack)->op != OP_END) { fprintf(stderr, "Syntax error: unexpected '%s'\n", token_[token.type]); return ERR_PARSER; } tmp_psitem.type = top->type; tmp_psitem.value = top->value; ps_destroy(psa_stack); return OK; } int parse() { next(); strInit(&tmp_buffer); int err = P(); strFree(&tmp_buffer); return err; }