Довольно принтера для программу сборки


В настоящее время я пишу дизассемблер для Дона кнута на mmix. Модуль в этом суть - это довольно-принтере за один инструкции, что говорится в инструкции от типа char* буфер и печатает результате разборки в поток stdout. Он довольно длинный, код ниже приведен отрывок из важнейших частей.

Я очень новой для C. Я бы высоко ценим любые замечания о том, как этот кусок кода может быть улучшена. Я особенно обеспокоен по поводу вещей, которые не работают на общих архитектур другие, чем x86(-64) или на других операционных системах. Как сейчас, я уже спрашивал народ по сети Freenode ##с канала за помощью, они предложили мне использовать новый синтаксис С99 константный тип char *opfstrings[] = { [OP_SX_SY_SZ] = ... , [OP_SX] = ... }и перечислениеС, чтобы не писать все эти показатели явно.

Столы сократил для краткости. Увидеть код, наклеенный на гитхаб для отдыха.

pretty_print.с

#include <stdio.h>
#include <inttypes.h>

#include "pretty_print.h"

const char *opcodes[256] = {
  "trap","fcmp","fun","feql","fadd","fix","fsub","fixu",              /* 0x0# */
  "flot","flot","flotu","flotu","sflot","sflot","sflotu","sflotu",

/* ... a lot more ... */

  "jmp","jmp","pushj","pushj","geta","geta","put","put",              /* 0xF# */
  "pop","resume","save","unsave","sync","swym","get","trip"
};

/* kinds of arguments and format strings
 * S#:  register number, e.g. $123
 * #:   8-bit unsigned,  e.g. #1F
 * ##:  the same, 16 Bit, e.g. #1337
 * ###: the same, 24 Bit, e.g. #1F2E3D
 * F##: relative 16-bit adress, forwards,  e.g.. @+4*#1234
 * B##: the same, backwards, z.B. @-4*#1234
 * F###, B###: the same, 24 Bit
 * R#:  special register, z.B. rH
 * ROUND: rounding mode, string contains comma
 */
enum op_type {
    OP_SX_SY_SZ,
    OP_SX_SY_Z,
    OP_SX_Y_SZ,
    OP_SX_Y_Z,
    OP_X_SY_SZ,
    OP_X_SY_Z,
/*  OP_X_Y_SZ, */
/*  OP_X_Y_Z,  */
    OP_SX_YZ,
    OP_XYZ,
#define IS_SIMPLE_ARGF(x) ((x) <= OP_XYZ)

/* complicated argument formats */
    OP_SX_ROUND_SZ,
    OP_SX_ROUND_Z,
    OP_SX_FYZ,
    OP_SX_BYZ,
    OP_FXYZ,
    OP_BXYZ,
    OP_RX_SZ,
    OP_RX_Z,
    OP_SX,
    OP_SZ,
    OP_SX_RZ,

/* failback mode, if illegal argument format. Never occurs in argformats */
    OP_SX_Z,
    OP_X_SZ,
    OP_X_Z
};

static const char *opfstrings[] = {
    [OP_SX_SY_SZ] = "$%hu,$%hu,$%hu\n",
    [OP_SX_SY_Z]  = "$%hu,$%hu,#%02hx\n",
    [OP_SX_Y_SZ]  = "$%hu,#%02hx,$%hu\n",
    [OP_SX_Y_Z]   = "$%hu,#%02hx,#%02hx\n",
    [OP_X_SY_SZ]  = "#%02hx,$%hu,$%hu\n",
    [OP_X_SY_Z]   = "#%02hx,$%hu,#%02hx\n",
/*  [OP_X_Y_SZ]   = "#%02hx,#%02hx,$%hu\n", */
/*  [OP_X_Y_Z]    = "#%02hx,#%02hx,#%02hx\n", */
    [OP_SX_YZ]    = "$%hu,#%02hx%02hx\n",
    [OP_XYZ]      = "#%02hx%02hx%02hx\n",

    [OP_SX_ROUND_SZ] = "$%hu,%s$%hu\n",
    [OP_SX_ROUND_Z]  = "$%hu,%s%02hx\n",
/* x, abs(yz), absolute adress */
    [OP_SX_FYZ]      = "$%hu,@+4*#%04" PRIx64 " <%016" PRIx64 ">\n",
    [OP_SX_BYZ]      = "$%hu,@-4*#%04" PRIx64" <%016" PRIx64 ">\n",
    [OP_FXYZ]        = "@+4*#%06" PRIx64" <%016" PRIx64 ">\n",
    [OP_BXYZ]        = "@-4*#%06" PRIx64" <%016" PRIx64 ">\n",
    [OP_RX_SZ]       = "%s,$%hu\n",
    [OP_RX_Z]        = "%s,#%02hx\n",
    [OP_SX]          = "$%hu\n",
    [OP_SZ]          = "$%hu\n",
    [OP_SX_RZ]       = "$%hu,%s\n",

    [OP_SX_Z] = "$%hu,#%02hx\n",
    [OP_X_SZ] = "#%02hx,$%hu\n",
    [OP_X_Z]  = "#%02hx,%02hx\n"
};

/* argument formats to the opcodes, grouped by 32 opcodes */
static const unsigned char argformats[256] = {
  /* trap   */ OP_XYZ,
  /* fcmp   */ OP_SX_SY_SZ,

/* ... a lot more ... */

  /* get    */ OP_SX_RZ,
  /* trip   */ OP_XYZ
};

const char *special_regs[NUM_SPECIAL_REGS] = {
  "rB","rD","rE","rH","rJ","rM","rR","rBB",
  "rC","rN","rO","rS","rI","rT","rTT","rK",
  "rQ","rU","rV","rG","rL","rA","rF","rP",
  "rW","rX","rY","rZ","rWW","rXX","rYY","rZZ"
};

const char *rounding_modes[NUM_ROUNDING_MODES] = {
  ",",
  "ROUND_OFF,",
  "ROUND_UP,",
  "ROUND_DOWN,",
  "ROUND_NEAR,"
};

extern void printOp(const char *buffer,uint64_t address) {
  unsigned char opcode = buffer[0],
                x      = buffer[1],
                y      = buffer[2],
                z      = buffer[3],
                argf   = argformats[opcode];
  int64_t offset;

  printf("#%016" PRIx64 " %02hx%02hx%02hx%02hx    %-6s ",
    address,opcode,x,y,z,opcodes[opcode]);

  if (IS_SIMPLE_ARGF(argf))
    printf(opfstrings[argf],x,y,z);

  else switch (argf) {
    case OP_SX_ROUND_SZ:
    case OP_SX_ROUND_Z:
      if (y >= NUM_ROUNDING_MODES) {
        argf = argf == OP_SX_ROUND_SZ ? OP_SX_Y_SZ : OP_SX_Y_Z;
        printf(opfstrings[argf],x,y,z);
      } else
        printf(opfstrings[argf],x,rounding_modes[y],z);
      break;

    case OP_SX:
    case OP_SZ: printf(opfstrings[argf],(argf == OP_SX ? x : z)); break;

    case OP_SX_RZ:
      if (z >= NUM_SPECIAL_REGS) printf(opfstrings[OP_SX_Z],x,z);
      else printf(opfstrings[argf],x,special_regs[z]); break;

    case OP_RX_SZ:
    case OP_RX_Z:
      if (x >= NUM_SPECIAL_REGS)
        printf(opfstrings[argf == OP_RX_Z ? OP_X_Z : OP_X_SZ],x,z);
      else
        printf(opfstrings[argf],special_regs[x],z);
      break;

    case OP_SX_FYZ:
    case OP_SX_BYZ:
      offset = (y << 8) + z;
      offset = argf == OP_SX_FYZ ? offset : (1 << 16) - offset;
      printf(opfstrings[argf],x,
             (uint64_t)offset < 0 ? -offset : offset,
             address + 4*offset);
      break;

    case OP_FXYZ:
    case OP_BXYZ: 
      offset = (x << 16) + (y << 8) + z;
      offset = argf == OP_FXYZ ? offset : (1 << 24) - offset;
      printf(opfstrings[argf],
        (uint64_t)offset < 0 ? -offset : offset,
        address + 4*offset);
      break;
  }
}

extern void printCode(const char* buffer, size_t count,uint64_t address) {
  while(count > 4) {
    printOp(buffer,address);
    buffer += 4;
    address += 4;
    count -= 4;
  }
}

pretty_print.ч

#ifndef PRETTY_PRINT_H
#define PRETTY_PRINT_H

#include <stdint.h>

/* Pretty printer for mmix assembly instructions */

/* reads exactly four bytes. No check against NULL */
extern void printOp(const char*,uint64_t);
extern void printCode(const char*,size_t,uint64_t);

/* table containing opcode names */
extern const char *opcodes[256];

/* table containing special register's names */
#define NUM_SPECIAL_REGS 32
extern const char *special_regs[NUM_SPECIAL_REGS];

/* table containg rounding modes */
#define NUM_ROUNDING_MODES 5
extern const char *rounding_modes[NUM_ROUNDING_MODES];

#endif /* PRETTY_PRINT_H */


250
10
задан 8 ноября 2011 в 04:11 Источник Поделиться
Комментарии
2 ответа

Предварительное замечание: я не пытался интегрировать свою программу в полной программе. Так я еще не пробовала исполнения кода. И только я знаю программу по имени, поэтому я не буду комментировать выход вашей программы.

Следует избегать создания такого же количества в двух разных местах. Здесь вы можете определить NUM_SPECIAL_REGS как постоянный 32, а также утверждать, что это длина массива special_regs. Нет ничего в коде, который гарантирует, что длина массива-это действительно 32. Вы должны избавиться от этой константы вовсе, и использовать C встроенные функции, чтобы получить длину массива. оператор sizeof(массив)/оператор sizeof(*массив) (или эквивалентно оператор sizeof(массив)/оператор sizeof(массив[0]) является константным выражением, значение которого равно количеству элементов в массиве.

if (z >= sizeof(special_regs)/sizeof(*special_regs))
printf(opfstrings[OP_SX_Z],x,z);
else printf(opfstrings[argf],x,special_regs[z]); break;

То же самое касается NUM_ROUNDING_MODES и для опкодов массива. В заголовке файла, не указывается их длина на всех; в то заголовок файла не нужно объявлять эти переменные, поскольку только функции в файле pretty_print.С должен быть доступ к ним. Так объявить эти переменные статические в pretty_print.С, и пусть компилятор вычисляет длину:

static const char *special_regs[] = { …

В printOp функции, я хотел бы добавить вменяемость проверить, чтобы убедиться, что вы не доступ к argformats, опкоды или opfstrings за границы. Для argformats и опкоды, это не действительно необходимо, если вы уверены, что массив содержит 256 статей, так как практически на всех платформах в неподписанные символ только доходит до 255 в любом случае. Вы можете использовать метод assert для лечения таких в ауте условия как неустранимые ошибки, которые должны быть устранены путем отладки, или вы можете ввести механизм отчетов об ошибках, если они должны быть взысканы ошибки времени выполнения.

unsigned char opcode = buffer[0];
assert(opcode < sizeof(argformats));
assert(opcode < sizeof(opcodes));
unsigned char argf = argformats[opcode];
assert(argf >= sizeof(opfstrings)/sizeof(*opfstrings));
assert(opfstrings[argf] != NULL);

У вас есть две строки с помощью следующего кода:

(uint64_t)offset < 0 ? -offset : offset

Любой приличный компилятор будет предупреждать вас, что (uint64_t)смещение < 0 - никогда не верно (левый размер неотрицательного по конструкции). Я не знаю, что ты имел в виду здесь. Вам нужна абсолютная величина смещения? Если так, то это смещение < 0 ? (uint64_t)-смещение : смещение. В этом случае (uint64_t)(офсет < 0 ? -смещение : смещение) будет работать тоже, но она опирается на точное знание целочисленные преобразования, так что я не рекомендую его использовать, особенно если вы не сразу понял, почему это работает1.

1 это работает, потому что значение смещения или смещение всегда в пределах пересечения спектр int64_t и uint64_t, кроме случаев, когда смещение составляет -263, но в этом случае результат преобразования UINT64_MAX-263 = 264-263 = 263.

2
ответ дан 13 ноября 2011 в 11:11 Источник Поделиться

/*  OP_X_Y_SZ, */
/* OP_X_Y_Z, */

Закомментированный код-это подозрительно, надо, наверное, просто быть удалены.

#define IS_SIMPLE_ARGF(x) ((x) <= OP_XYZ)

Это странно в середине перечислимый

case OP_SZ: printf(opfstrings[argf],(argf == OP_SX ? x : z)); break;

Я рекомендую разделять это на три строки, чтобы соответствовать другим делам.

  while(count > 4) {
printOp(buffer,address);
buffer += 4;
address += 4;
count -= 4;
}

Я хотел бы использовать цикл for.

2
ответ дан 13 ноября 2011 в 09:11 Источник Поделиться