nkeynes@1006 | 1 | /**
|
nkeynes@1006 | 2 | * $Id: target.c 931 2008-10-31 02:57:59Z nkeynes $
|
nkeynes@1006 | 3 | *
|
nkeynes@1006 | 4 | * Target code-generation support - provides a generic harness around the raw
|
nkeynes@1006 | 5 | * (machine-specific) code emitter.
|
nkeynes@1006 | 6 | *
|
nkeynes@1006 | 7 | * Copyright (c) 2009 Nathan Keynes.
|
nkeynes@1006 | 8 | *
|
nkeynes@1006 | 9 | * This program is free software; you can redistribute it and/or modify
|
nkeynes@1006 | 10 | * it under the terms of the GNU General Public License as published by
|
nkeynes@1006 | 11 | * the Free Software Foundation; either version 2 of the License, or
|
nkeynes@1006 | 12 | * (at your option) any later version.
|
nkeynes@1006 | 13 | *
|
nkeynes@1006 | 14 | * This program is distributed in the hope that it will be useful,
|
nkeynes@1006 | 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
nkeynes@1006 | 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
nkeynes@1006 | 17 | * GNU General Public License for more details.
|
nkeynes@1006 | 18 | */
|
nkeynes@1006 | 19 |
|
nkeynes@1006 | 20 | #include <stdlib.h>
|
nkeynes@1006 | 21 |
|
nkeynes@1006 | 22 | #include "lxdream.h"
|
nkeynes@1006 | 23 | #include "xlat/xir.h"
|
nkeynes@1006 | 24 | #include "xlat/machine.h"
|
nkeynes@1006 | 25 |
|
nkeynes@1006 | 26 | #define DEFAULT_FIXUP_TABLE_SIZE 4096
|
nkeynes@1006 | 27 | #define ALIGN32(p) p += ((-(uintptr_t)p)&0x03)
|
nkeynes@1006 | 28 | #define ALIGN64(p) p += ((-(uintptr_t)p)&0x07)
|
nkeynes@1006 | 29 |
|
nkeynes@1006 | 30 | /**
|
nkeynes@1006 | 31 | * Currently we use a single static target_data so that we can reuse the
|
nkeynes@1006 | 32 | * allocated memory (and we only do one codegen at a time anyway). However
|
nkeynes@1006 | 33 | * we keep this private so that other modules can't assume there's only one TD.
|
nkeynes@1006 | 34 | */
|
nkeynes@1006 | 35 | static struct target_data TD;
|
nkeynes@1006 | 36 |
|
nkeynes@1006 | 37 | /**
|
nkeynes@1006 | 38 | * Add a new fixup without setting a target value
|
nkeynes@1006 | 39 | */
|
nkeynes@1006 | 40 | static target_fixup_t target_add_fixup( target_data_t td, int type, void *location )
|
nkeynes@1006 | 41 | {
|
nkeynes@1006 | 42 | if( td->fixup_table_posn == td->fixup_table_size ) {
|
nkeynes@1006 | 43 | td->fixup_table_size <<= 1;
|
nkeynes@1006 | 44 | td->fixup_table = realloc(td->fixup_table, td->fixup_table_size * sizeof(struct target_fixup_struct));
|
nkeynes@1006 | 45 | assert( td->fixup_table != NULL );
|
nkeynes@1006 | 46 | }
|
nkeynes@1006 | 47 | target_fixup_t fixup = &td->fixup_table[td->fixup_table_posn++];
|
nkeynes@1006 | 48 | fixup->fixup_type = type | TARGET_FIXUP_CONST32;
|
nkeynes@1006 | 49 | fixup->fixup_offset = ((uint8_t *)location) - (uint8_t *)&td->block->code[0];
|
nkeynes@1006 | 50 | return fixup;
|
nkeynes@1006 | 51 | }
|
nkeynes@1006 | 52 |
|
nkeynes@1006 | 53 | void target_add_const32_fixup( target_data_t td, int mode, void *location, uint32_t i )
|
nkeynes@1006 | 54 | {
|
nkeynes@1006 | 55 | target_add_fixup(td, mode|TARGET_FIXUP_CONST32, location)->value.i = i;
|
nkeynes@1006 | 56 | }
|
nkeynes@1006 | 57 |
|
nkeynes@1006 | 58 | void target_add_const64_fixup( target_data_t td, int mode, void *location, uint64_t q )
|
nkeynes@1006 | 59 | {
|
nkeynes@1006 | 60 | target_add_fixup(td, mode|TARGET_FIXUP_CONST64, location)->value.q = q;
|
nkeynes@1006 | 61 | }
|
nkeynes@1006 | 62 |
|
nkeynes@1006 | 63 | void target_add_raise_fixup( target_data_t td, int type, void *location, xir_op_t *exc )
|
nkeynes@1006 | 64 | {
|
nkeynes@1006 | 65 | target_add_fixup(td, mode|TARGET_FIXUP_RAISE, location)->value.exc = exc;
|
nkeynes@1006 | 66 | }
|
nkeynes@1006 | 67 |
|
nkeynes@1006 | 68 | void target_add_raiseext_fixup( target_data_t td, int type, void *location, xir_op_t *exc )
|
nkeynes@1006 | 69 | {
|
nkeynes@1006 | 70 | target_add_fixup(td, mode|TARGET_FIXUP_RAISEEXT, location)->value.exc = exc;
|
nkeynes@1006 | 71 | }
|
nkeynes@1006 | 72 |
|
nkeynes@1006 | 73 | void target_add_offset_fixup( target_data_t td, int type, void *location, uint32_t off )
|
nkeynes@1006 | 74 | {
|
nkeynes@1006 | 75 | target_add_fixup(td, mode|TARGET_FIXUP_OFFSET, location)->target_offset = off;
|
nkeynes@1006 | 76 | }
|
nkeynes@1006 | 77 |
|
nkeynes@1006 | 78 | void target_add_pointer_fixup( target_data_t td, int type, void *location, void *p )
|
nkeynes@1006 | 79 | {
|
nkeynes@1006 | 80 | target_add_fixup(td, mode|TARGET_FIXUP_POINTER, location)->value.p = p;
|
nkeynes@1006 | 81 | }
|
nkeynes@1006 | 82 |
|
nkeynes@1006 | 83 |
|
nkeynes@1006 | 84 |
|
nkeynes@1006 | 85 | void target_ensure_space( target_data_t td, int space_required )
|
nkeynes@1006 | 86 | {
|
nkeynes@1006 | 87 | uint8_t *oldstart = td->block->code;
|
nkeynes@1006 | 88 | uint32_t new_size = (td->xlat_output - oldstart) + space_required;
|
nkeynes@1006 | 89 | if( new_size < td->block->size ) {
|
nkeynes@1006 | 90 | xlat_current_block = xlat_extend_block( xlat_output - oldstart + MAX_INSTRUCTION_SIZE );
|
nkeynes@1006 | 91 | eob = xlat_current_block->code + xlat_current_block->size;
|
nkeynes@1006 | 92 | td->block = xlat_extend_block( new_size );
|
nkeynes@1006 | 93 | xlat_output = td->block->code + (xlat_output - oldstart);
|
nkeynes@1006 | 94 | }
|
nkeynes@1006 | 95 | }
|
nkeynes@1006 | 96 |
|
nkeynes@1006 | 97 | /**
|
nkeynes@1006 | 98 | * Generate the exception table and exception bodies from the fixup data
|
nkeynes@1006 | 99 | * Note that this may add additional constants to the fixup table.
|
nkeynes@1006 | 100 | */
|
nkeynes@1006 | 101 | static void target_gen_exception_table( )
|
nkeynes@1006 | 102 | {
|
nkeynes@1006 | 103 | int exc_size = 0, num_raiseext = 0;
|
nkeynes@1006 | 104 |
|
nkeynes@1006 | 105 | for( target_fixup_t fixup = &TD.fixup_table[0]; fixup != &TD.fixup_table[TD.fixup_table_posn]; fixup++ ) {
|
nkeynes@1006 | 106 | int type =
|
nkeynes@1006 | 107 | switch(TARGET_FIXUP_TARGET(fixup->type)) {
|
nkeynes@1006 | 108 | case TARGET_FIXUP_RAISEEXT:
|
nkeynes@1006 | 109 | num_raiseext++;
|
nkeynes@1006 | 110 | /* fallthrough */
|
nkeynes@1006 | 111 | case TARGET_FIXUP_RAISE:
|
nkeynes@1006 | 112 | exc_size += TD.get_code_size(fixup->value.exc, NULL);
|
nkeynes@1006 | 113 | }
|
nkeynes@1006 | 114 | }
|
nkeynes@1006 | 115 |
|
nkeynes@1006 | 116 | ALIGN64(TD.xlat_output);
|
nkeynes@1006 | 117 | target_ensure_space( td, exc_size + num_raiseext*sizeof(struct xlat_exception_record) );
|
nkeynes@1006 | 118 | uint8_t *blockstart = &TD.block->code[0];
|
nkeynes@1006 | 119 | struct xlat_exception_record *exc_record = (struct xlat_exception_record *)TD.xlat_output;
|
nkeynes@1006 | 120 | TD.block->exc_table_offset = TD.xlat_output - blockstart;
|
nkeynes@1006 | 121 | TD.block->exc_table_size = num_raiseext;
|
nkeynes@1006 | 122 | TD.xlat_output += (num_raiseext*sizeof(struct xlat_exception_record));
|
nkeynes@1006 | 123 |
|
nkeynes@1006 | 124 | for( target_fixup_t fixup = &TD.fixup_table[0]; fixup != &td_fixup_table[TD.fixup_table_posn]; fixup++ ) {
|
nkeynes@1006 | 125 | switch( TARGET_FIXUP_TARGET(fixup->type) ) {
|
nkeynes@1006 | 126 | case TARGET_FIXUP_RAISEEXT:
|
nkeynes@1006 | 127 | exc_record->xlat_pc_offset = fixup->fixup_offset + 4;
|
nkeynes@1006 | 128 | exc_record->xlat_exc_offset = TD.xlat_output - blockstart;
|
nkeynes@1006 | 129 | /* fallthrough */
|
nkeynes@1006 | 130 | case TARGET_FIXUP_RAISE:
|
nkeynes@1006 | 131 | fixup->target_offset = TD.xlat_output - blockstart;
|
nkeynes@1006 | 132 | TD.codegen( td, fixup->value.exc, NULL );
|
nkeynes@1006 | 133 | }
|
nkeynes@1006 | 134 | }
|
nkeynes@1006 | 135 | }
|
nkeynes@1006 | 136 |
|
nkeynes@1006 | 137 | /**
|
nkeynes@1006 | 138 | * Generate constant table from the fixup data.
|
nkeynes@1006 | 139 | */
|
nkeynes@1006 | 140 | static void target_gen_constant_table( )
|
nkeynes@1006 | 141 | {
|
nkeynes@1006 | 142 | int numconst32=0, numconst64=0;
|
nkeynes@1006 | 143 |
|
nkeynes@1006 | 144 | /* Determine table size */
|
nkeynes@1006 | 145 | for( target_fixup_t fixup = &TD.fixup_table[0]; fixup != &td_fixup_table[TD.fixup_table_posn]; fixup++ ) {
|
nkeynes@1006 | 146 | int type = TARGET_FIXUP_TARGET(fixup->type);
|
nkeynes@1006 | 147 | if( type == TARGET_FIXUP_CONST32 ) {
|
nkeynes@1006 | 148 | numconst32++;
|
nkeynes@1006 | 149 | } else if( type == TARGET_FIXUP_CONST64 ) {
|
nkeynes@1006 | 150 | numconst64++;
|
nkeynes@1006 | 151 | }
|
nkeynes@1006 | 152 | }
|
nkeynes@1006 | 153 |
|
nkeynes@1006 | 154 | if( numconst64 != 0 ) {
|
nkeynes@1006 | 155 | ALIGN64(TD.xlat_output);
|
nkeynes@1006 | 156 | } else if( numconst32 != 0 ) {
|
nkeynes@1006 | 157 | ALIGN32(TD.xlat_output);
|
nkeynes@1006 | 158 | } else {
|
nkeynes@1006 | 159 | return; /* no constants */
|
nkeynes@1006 | 160 | }
|
nkeynes@1006 | 161 | target_ensure_space( td, numconst64*8 + numconst32*4 );
|
nkeynes@1006 | 162 | uint8_t *blockstart = &TD.block->code[0];
|
nkeynes@1006 | 163 |
|
nkeynes@1006 | 164 | /* TODO: Merge reused constant values */
|
nkeynes@1006 | 165 | uint64_t *const64p = (uint64_t *)TD.xlat_output;
|
nkeynes@1006 | 166 | uint32_t *const32p = (uint32_t *)(TD.xlat_output + numconst64*8);
|
nkeynes@1006 | 167 | TD.xlat_output += (numconst64*8 + numconst32*4);
|
nkeynes@1006 | 168 |
|
nkeynes@1006 | 169 | for( target_fixup_t fixup = &TD.fixup_table[0]; fixup != &td_fixup_table[TD.fixup_table_posn]; fixup++ ) {
|
nkeynes@1006 | 170 | switch(TARGET_FIXUP_TARGET(fixup->type)) {
|
nkeynes@1006 | 171 | case TARGET_FIXUP_CONST32:
|
nkeynes@1006 | 172 | fixup->target_offset = ((uint8_t *)const32p) - blockstart;
|
nkeynes@1006 | 173 | *const32p++ = fixup->value.i;
|
nkeynes@1006 | 174 | break;
|
nkeynes@1006 | 175 | case TARGET_FIXUP_CONST64:
|
nkeynes@1006 | 176 | fixup->target_offset = ((uint8_t *)const64p) - blockstart;
|
nkeynes@1006 | 177 | *const64p++ = fixup->value.q;
|
nkeynes@1006 | 178 | break;
|
nkeynes@1006 | 179 | }
|
nkeynes@1006 | 180 | }
|
nkeynes@1006 | 181 | }
|
nkeynes@1006 | 182 |
|
nkeynes@1006 | 183 | /**
|
nkeynes@1006 | 184 | * Apply all target fixups - assumes exceptions + constants have already been
|
nkeynes@1006 | 185 | * generated.
|
nkeynes@1006 | 186 | */
|
nkeynes@1006 | 187 | static void target_apply_fixups( )
|
nkeynes@1006 | 188 | {
|
nkeynes@1006 | 189 | for( target_fixup_t fixup = &TD.fixup_table[0]; fixup != &TD.fixup_table[TD.fixup_table_posn]; fixup++ ) {
|
nkeynes@1006 | 190 | void *target;
|
nkeynes@1006 | 191 | if( TARGET_FIXUP_TARGET(fixup->type) == TARGET_FIXUP_POINTER ) {
|
nkeynes@1006 | 192 | target = fixup->value.p;
|
nkeynes@1006 | 193 | } else {
|
nkeynes@1006 | 194 | target = &TD.block->code[fixup->target_offset];
|
nkeynes@1006 | 195 | }
|
nkeynes@1006 | 196 |
|
nkeynes@1006 | 197 | uint32_t *loc = (uint32_t *)TD.block->code[fixup->fixup_offset];
|
nkeynes@1006 | 198 | uint64_t *loc64 = (uint64_t *)TD.block->code[fixup->fixup_offset];
|
nkeynes@1006 | 199 | switch(TARGET_FIXUP_MODE(fixup->fixup_type)) {
|
nkeynes@1006 | 200 | case TARGET_FIXUP_REL32:
|
nkeynes@1006 | 201 | *loc += (uint8_t *)target - (uint8_t *)(loc+1);
|
nkeynes@1006 | 202 | break;
|
nkeynes@1006 | 203 | case TARGET_FIXUP_REL64:
|
nkeynes@1006 | 204 | *loc64 += (uint8_t *)target - (uint8_t *)(loc64+1);
|
nkeynes@1006 | 205 | break;
|
nkeynes@1006 | 206 | case TARGET_FIXUP_ABS32:
|
nkeynes@1006 | 207 | *loc += (uint32_t)target;
|
nkeynes@1006 | 208 | break;
|
nkeynes@1006 | 209 | case TARGET_FIXUP_ABS64:
|
nkeynes@1006 | 210 | *loc64 += (uint64_t)target;
|
nkeynes@1006 | 211 | break;
|
nkeynes@1006 | 212 | }
|
nkeynes@1006 | 213 | }
|
nkeynes@1006 | 214 | }
|
nkeynes@1006 | 215 |
|
nkeynes@1006 | 216 | void target_codegen( xlat_target_machine_t machine, source_data_t sd )
|
nkeynes@1006 | 217 | {
|
nkeynes@1006 | 218 | /* Setup the target data struct */
|
nkeynes@1006 | 219 | TD.mach = machine;
|
nkeynes@1006 | 220 | TD.src = sd->machine;
|
nkeynes@1006 | 221 | TD.block = xlat_start_block( sd->pc_start );
|
nkeynes@1006 | 222 | TD.xlat_output = &TD.block->code[0];
|
nkeynes@1006 | 223 | if( TD.fixup_table == NULL ) {
|
nkeynes@1006 | 224 | if( TD.fixup_table == NULL ) {
|
nkeynes@1006 | 225 | TD.fixup_table_size = DEFAULT_FIXUP_TABLE_SIZE;
|
nkeynes@1006 | 226 | TD.fixup_table = malloc( td->fixup_table_size * sizeof(struct target_fixup_struct) );
|
nkeynes@1006 | 227 | assert( TD.fixup_table != NULL );
|
nkeynes@1006 | 228 | }
|
nkeynes@1006 | 229 | }
|
nkeynes@1006 | 230 | TD.fixup_table_posn = 0;
|
nkeynes@1006 | 231 |
|
nkeynes@1006 | 232 | uint32_t code_size = machine->get_code_size(sd->ir_begin,sd->ir_end);
|
nkeynes@1006 | 233 | target_ensure_space(&TD, code_size);
|
nkeynes@1006 | 234 |
|
nkeynes@1006 | 235 | machine->codegen(&TD, sd->begin, sd->end);
|
nkeynes@1006 | 236 |
|
nkeynes@1006 | 237 | target_gen_exception_table();
|
nkeynes@1006 | 238 | target_gen_constant_table();
|
nkeynes@1006 | 239 | target_apply_fixups();
|
nkeynes@1006 | 240 |
|
nkeynes@1006 | 241 | xlat_commit_block( TD.xlat_output - &TD.block->code[0], sd->pc_end-sd->pc_start );
|
nkeynes@1006 | 242 | return &TD.block->code[0];
|
nkeynes@1006 | 243 | }
|