Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * Copyright 2011 Tom Stellard <tstellar@gmail.com>
  3.  * Copyright 2013 Advanced Micro Devices, Inc.
  4.  *
  5.  * All Rights Reserved.
  6.  *
  7.  * Permission is hereby granted, free of charge, to any person obtaining
  8.  * a copy of this software and associated documentation files (the
  9.  * "Software"), to deal in the Software without restriction, including
  10.  * without limitation the rights to use, copy, modify, merge, publish,
  11.  * distribute, sublicense, and/or sell copies of the Software, and to
  12.  * permit persons to whom the Software is furnished to do so, subject to
  13.  * the following conditions:
  14.  *
  15.  * The above copyright notice and this permission notice (including the
  16.  * next paragraph) shall be included in all copies or substantial
  17.  * portions of the Software.
  18.  *
  19.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  20.  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  21.  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  22.  * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
  23.  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  24.  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  25.  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  26.  *
  27.  * Author: Tom Stellard <thomas.stellard@amd.com>
  28.  */
  29.  
  30. #include <errno.h>
  31. #include <regex.h>
  32. #include <stdlib.h>
  33. #include <stdio.h>
  34. #include <string.h>
  35. #include <sys/types.h>
  36.  
  37. #include "r500_fragprog.h"
  38. #include "r300_fragprog_swizzle.h"
  39. #include "radeon_compiler.h"
  40. #include "radeon_compiler_util.h"
  41. #include "radeon_opcodes.h"
  42. #include "radeon_program.h"
  43. #include "radeon_regalloc.h"
  44. #include "radeon_swizzle.h"
  45. #include "util/u_math.h"
  46.  
  47. #include "rc_test_helpers.h"
  48.  
  49. /* This file contains some helper functions for filling out the rc_instruction
  50.  * data structures.  These functions take a string as input based on the format
  51.  * output by rc_program_print().
  52.  */
  53.  
  54. #define VERBOSE 0
  55.  
  56. #define DBG(...) do { if (VERBOSE) fprintf(stderr, __VA_ARGS__); } while(0)
  57.  
  58. #define REGEX_ERR_BUF_SIZE 50
  59.  
  60. struct match_info {
  61.         const char * String;
  62.         int Length;
  63. };
  64.  
  65. static int is_whitespace(const char *str)
  66. {
  67.         regex_t regex;
  68.         if (regcomp(&regex, "^[ \n]+$", REG_EXTENDED)) {
  69.                 fprintf(stderr, "Failed to compile whitespace regex\n");
  70.                 return 0;
  71.         }
  72.         return regexec(&regex, str, 0, NULL, 0) != REG_NOMATCH;
  73. }
  74.  
  75. static int match_length(regmatch_t * matches, int index)
  76. {
  77.         return matches[index].rm_eo - matches[index].rm_so;
  78. }
  79.  
  80. static int regex_helper(
  81.         const char * regex_str,
  82.         const char * search_str,
  83.         regmatch_t * matches,
  84.         int num_matches)
  85. {
  86.         char err_buf[REGEX_ERR_BUF_SIZE];
  87.         regex_t regex;
  88.         int err_code;
  89.         unsigned int i;
  90.  
  91.         err_code = regcomp(&regex, regex_str, REG_EXTENDED);
  92.         if (err_code) {
  93.                 regerror(err_code, &regex, err_buf, REGEX_ERR_BUF_SIZE);
  94.                 fprintf(stderr, "Failed to compile regex: %s\n", err_buf);
  95.                 return 0;
  96.         }
  97.  
  98.         err_code = regexec(&regex, search_str, num_matches, matches, 0);
  99.         DBG("Search string: '%s'\n", search_str);
  100.         for (i = 0; i < num_matches; i++) {
  101.                 DBG("Match %u start = %d end = %d\n", i,
  102.                                         matches[i].rm_so, matches[i].rm_eo);
  103.         }
  104.         if (err_code) {
  105.                 regerror(err_code, &regex, err_buf, REGEX_ERR_BUF_SIZE);
  106.                 fprintf(stderr, "Failed to match regex: %s\n", err_buf);
  107.                 return 0;
  108.         }
  109.         return 1;
  110. }
  111.  
  112. #define REGEX_SRC_MATCHES 6
  113.  
  114. struct src_tokens {
  115.         struct match_info Negate;
  116.         struct match_info Abs;
  117.         struct match_info File;
  118.         struct match_info Index;
  119.         struct match_info Swizzle;
  120. };
  121.  
  122. /**
  123.  * Initialize the source register at index src_index for the instruction based
  124.  * on src_str.
  125.  *
  126.  * NOTE: Warning in init_rc_normal_instruction() applies to this function as
  127.  * well.
  128.  *
  129.  * @param src_str A string that represents the source register.  The format for
  130.  * this string is the same that is output by rc_program_print.
  131.  * @return 1 On success, 0 on failure
  132.  */
  133. int init_rc_normal_src(
  134.         struct rc_instruction * inst,
  135.         unsigned int src_index,
  136.         const char * src_str)
  137. {
  138.         const char * regex_str = "(-*)(\\|*)([[:lower:]]*)\\[*([[:digit:]]*)\\]*(\\.*[[:lower:]_]*)";
  139.         regmatch_t matches[REGEX_SRC_MATCHES];
  140.         struct src_tokens tokens;
  141.         struct rc_src_register * src_reg = &inst->U.I.SrcReg[src_index];
  142.         unsigned int i;
  143.  
  144.         /* Execute the regex */
  145.         if (!regex_helper(regex_str, src_str, matches, REGEX_SRC_MATCHES)) {
  146.                 fprintf(stderr, "Failed to execute regex for src register.\n");
  147.                 return 0;
  148.         }
  149.  
  150.         /* Create Tokens */
  151.         tokens.Negate.String = src_str + matches[1].rm_so;
  152.         tokens.Negate.Length = match_length(matches, 1);
  153.         tokens.Abs.String = src_str + matches[2].rm_so;
  154.         tokens.Abs.Length = match_length(matches, 2);
  155.         tokens.File.String = src_str + matches[3].rm_so;
  156.         tokens.File.Length = match_length(matches, 3);
  157.         tokens.Index.String = src_str + matches[4].rm_so;
  158.         tokens.Index.Length = match_length(matches, 4);
  159.         tokens.Swizzle.String = src_str + matches[5].rm_so;
  160.         tokens.Swizzle.Length = match_length(matches, 5);
  161.  
  162.         /* Negate */
  163.         if (tokens.Negate.Length  > 0) {
  164.                 src_reg->Negate = RC_MASK_XYZW;
  165.         }
  166.  
  167.         /* Abs */
  168.         if (tokens.Abs.Length > 0) {
  169.                 src_reg->Abs = 1;
  170.         }
  171.  
  172.         /* File */
  173.         if (!strncmp(tokens.File.String, "temp", tokens.File.Length)) {
  174.                 src_reg->File = RC_FILE_TEMPORARY;
  175.         } else if (!strncmp(tokens.File.String, "input", tokens.File.Length)) {
  176.                 src_reg->File = RC_FILE_INPUT;
  177.         } else if (!strncmp(tokens.File.String, "const", tokens.File.Length)) {
  178.                 src_reg->File = RC_FILE_CONSTANT;
  179.         } else if (!strncmp(tokens.File.String, "none", tokens.File.Length)) {
  180.                 src_reg->File = RC_FILE_NONE;
  181.         }
  182.  
  183.         /* Index */
  184.         errno = 0;
  185.         src_reg->Index = strtol(tokens.Index.String, NULL, 10);
  186.         if (errno > 0) {
  187.                 fprintf(stderr, "Could not convert src register index.\n");
  188.                 return 0;
  189.         }
  190.  
  191.         /* Swizzle */
  192.         if (tokens.Swizzle.Length == 0) {
  193.                 src_reg->Swizzle = RC_SWIZZLE_XYZW;
  194.         } else {
  195.                 int str_index = 1;
  196.                 src_reg->Swizzle = RC_MAKE_SWIZZLE_SMEAR(RC_SWIZZLE_UNUSED);
  197.                 if (tokens.Swizzle.String[0] != '.') {
  198.                         fprintf(stderr, "First char of swizzle is not valid.\n");
  199.                         return 0;
  200.                 }
  201.                 for (i = 0; i < 4 && str_index < tokens.Swizzle.Length;
  202.                                                         i++, str_index++) {
  203.                         if (tokens.Swizzle.String[str_index] == '-') {
  204.                                 src_reg->Negate |= (1 << i);
  205.                                 str_index++;
  206.                         }
  207.                         switch(tokens.Swizzle.String[str_index]) {
  208.                         case 'x':
  209.                                 SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_X);
  210.                                 break;
  211.                         case 'y':
  212.                                 SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_Y);
  213.                                 break;
  214.                         case 'z':
  215.                                 SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_Z);
  216.                                 break;
  217.                         case 'w':
  218.                                 SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_W);
  219.                                 break;
  220.                         case '1':
  221.                                 SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_ONE);
  222.                                 break;
  223.                         case '0':
  224.                                 SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_ZERO);
  225.                                 break;
  226.                         case 'H':
  227.                                 SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_HALF);
  228.                                 break;
  229.                         case '_':
  230.                                 SET_SWZ(src_reg->Swizzle, i, RC_SWIZZLE_UNUSED);
  231.                                 break;
  232.                         default:
  233.                                 fprintf(stderr, "Unknown src register swizzle: %c\n",
  234.                                                 tokens.Swizzle.String[str_index]);
  235.                                 return 0;
  236.                         }
  237.                 }
  238.         }
  239.         DBG("File=%u index=%u swizzle=%x negate=%u abs=%u\n",
  240.                         src_reg->File, src_reg->Index, src_reg->Swizzle,
  241.                         src_reg->Negate, src_reg->Abs);
  242.         return 1;
  243. }
  244.  
  245. #define REGEX_DST_MATCHES 4
  246.  
  247. struct dst_tokens {
  248.         struct match_info File;
  249.         struct match_info Index;
  250.         struct match_info WriteMask;
  251. };
  252.  
  253. /**
  254.  * Initialize the destination for the instruction based on dst_str.
  255.  *
  256.  * NOTE: Warning in init_rc_normal_instruction() applies to this function as
  257.  * well.
  258.  *
  259.  * @param dst_str A string that represents the destination register.  The format
  260.  * for this string is the same that is output by rc_program_print.
  261.  * @return 1 On success, 0 on failure
  262.  */
  263. int init_rc_normal_dst(
  264.         struct rc_instruction * inst,
  265.         const char * dst_str)
  266. {
  267.         const char * regex_str = "([[:lower:]]*)\\[*([[:digit:]]*)\\]*(\\.*[[:lower:]]*)";
  268.         regmatch_t matches[REGEX_DST_MATCHES];
  269.         struct dst_tokens tokens;
  270.         unsigned int i;
  271.  
  272.         /* Execute the regex */
  273.         if (!regex_helper(regex_str, dst_str, matches, REGEX_DST_MATCHES)) {
  274.                 fprintf(stderr, "Failed to execute regex for dst register.\n");
  275.                 return 0;
  276.         }
  277.  
  278.         /* Create Tokens */
  279.         tokens.File.String = dst_str + matches[1].rm_so;
  280.         tokens.File.Length = match_length(matches, 1);
  281.         tokens.Index.String = dst_str + matches[2].rm_so;
  282.         tokens.Index.Length = match_length(matches, 2);
  283.         tokens.WriteMask.String = dst_str + matches[3].rm_so;
  284.         tokens.WriteMask.Length = match_length(matches, 3);
  285.  
  286.         /* File Type */
  287.         if (!strncmp(tokens.File.String, "temp", tokens.File.Length)) {
  288.                 inst->U.I.DstReg.File = RC_FILE_TEMPORARY;
  289.         } else if (!strncmp(tokens.File.String, "output", tokens.File.Length)) {
  290.                 inst->U.I.DstReg.File = RC_FILE_OUTPUT;
  291.         } else if (!strncmp(tokens.File.String, "none", tokens.File.Length)) {
  292.                 inst->U.I.DstReg.File = RC_FILE_NONE;
  293.                 return 1;
  294.         } else {
  295.                 fprintf(stderr, "Unknown dst register file type.\n");
  296.                 return 0;
  297.         }
  298.  
  299.         /* File Index */
  300.         errno = 0;
  301.         inst->U.I.DstReg.Index = strtol(tokens.Index.String, NULL, 10);
  302.  
  303.         if (errno > 0) {
  304.                 fprintf(stderr, "Could not convert dst register index\n");
  305.                 return 0;
  306.         }
  307.  
  308.         /* WriteMask */
  309.         if (tokens.WriteMask.Length == 0) {
  310.                 inst->U.I.DstReg.WriteMask = RC_MASK_XYZW;
  311.         } else {
  312.                 inst->U.I.DstReg.WriteMask = 0;
  313.                 /* The first character should be '.' */
  314.                 if (tokens.WriteMask.String[0] != '.') {
  315.                         fprintf(stderr, "1st char of writemask is not valid.\n");
  316.                         return 0;
  317.                 }
  318.                 for (i = 1; i < tokens.WriteMask.Length; i++) {
  319.                         switch(tokens.WriteMask.String[i]) {
  320.                         case 'x':
  321.                                 inst->U.I.DstReg.WriteMask |= RC_MASK_X;
  322.                                 break;
  323.                         case 'y':
  324.                                 inst->U.I.DstReg.WriteMask |= RC_MASK_Y;
  325.                                 break;
  326.                         case 'z':
  327.                                 inst->U.I.DstReg.WriteMask |= RC_MASK_Z;
  328.                                 break;
  329.                         case 'w':
  330.                                 inst->U.I.DstReg.WriteMask |= RC_MASK_W;
  331.                                 break;
  332.                         default:
  333.                                 fprintf(stderr, "Unknown swizzle in writemask: %c\n",
  334.                                                         tokens.WriteMask.String[i]);
  335.                                 return 0;
  336.                         }
  337.                 }
  338.         }
  339.         DBG("Dst Reg File=%u Index=%d Writemask=%d\n",
  340.                         inst->U.I.DstReg.File,
  341.                         inst->U.I.DstReg.Index,
  342.                         inst->U.I.DstReg.WriteMask);
  343.         return 1;
  344. }
  345.  
  346. #define REGEX_INST_MATCHES 7
  347. #define REGEX_CONST_MATCHES 5
  348.  
  349. struct inst_tokens {
  350.         struct match_info Opcode;
  351.         struct match_info Sat;
  352.         struct match_info Dst;
  353.         struct match_info Srcs[3];
  354. };
  355.  
  356. /**
  357.  * Initialize a normal instruction based on inst_str.
  358.  *
  359.  * WARNING: This function might not be able to handle every kind of format that
  360.  * rc_program_print() can output.  If you are having problems with a
  361.  * particular string, you may need to add support for it to this functions.
  362.  *
  363.  * @param inst_str A string that represents the source register.  The format for
  364.  * this string is the same that is output by rc_program_print.
  365.  * @return 1 On success, 0 on failure
  366.  */
  367.  
  368. int parse_rc_normal_instruction(
  369.         struct rc_instruction * inst,
  370.         const char * inst_str)
  371. {
  372.         const char * regex_str = "[[:digit:]: ]*([[:upper:][:digit:]]+)(_SAT)*[ ]*([^,;]*)[, ]*([^,;]*)[, ]*([^,;]*)[, ]*([^;]*)";
  373.         int i;
  374.         regmatch_t matches[REGEX_INST_MATCHES];
  375.         struct inst_tokens tokens;
  376.  
  377.         /* Execute the regex */
  378.         if (!regex_helper(regex_str, inst_str, matches, REGEX_INST_MATCHES)) {
  379.                 return 0;
  380.         }
  381.         memset(&tokens, 0, sizeof(tokens));
  382.  
  383.         /* Create Tokens */
  384.         tokens.Opcode.String = inst_str + matches[1].rm_so;
  385.         tokens.Opcode.Length = match_length(matches, 1);
  386.         if (matches[2].rm_so > -1) {
  387.                 tokens.Sat.String = inst_str + matches[2].rm_so;
  388.                 tokens.Sat.Length = match_length(matches, 2);
  389.         }
  390.  
  391.  
  392.         /* Fill out the rest of the instruction. */
  393.         inst->Type = RC_INSTRUCTION_NORMAL;
  394.  
  395.         for (i = 0; i < MAX_RC_OPCODE; i++) {
  396.                 const struct rc_opcode_info * info = rc_get_opcode_info(i);
  397.                 unsigned int first_src = 3;
  398.                 unsigned int j;
  399.                 if (strncmp(tokens.Opcode.String, info->Name, tokens.Opcode.Length)) {
  400.                         continue;
  401.                 }
  402.                 inst->U.I.Opcode = info->Opcode;
  403.                 if (info->HasDstReg) {
  404.                         char * dst_str;
  405.                         tokens.Dst.String = inst_str + matches[3].rm_so;
  406.                         tokens.Dst.Length = match_length(matches, 3);
  407.                         first_src++;
  408.  
  409.                         dst_str = malloc(sizeof(char) * (tokens.Dst.Length + 1));
  410.                         strncpy(dst_str, tokens.Dst.String, tokens.Dst.Length);
  411.                         dst_str[tokens.Dst.Length] = '\0';
  412.                         init_rc_normal_dst(inst, dst_str);
  413.                         free(dst_str);
  414.                 }
  415.                 for (j = 0; j < info->NumSrcRegs; j++) {
  416.                         char * src_str;
  417.                         tokens.Srcs[j].String =
  418.                                 inst_str + matches[first_src + j].rm_so;
  419.                         tokens.Srcs[j].Length =
  420.                                 match_length(matches, first_src + j);
  421.  
  422.                         src_str = malloc(sizeof(char) *
  423.                                                 (tokens.Srcs[j].Length + 1));
  424.                         strncpy(src_str, tokens.Srcs[j].String,
  425.                                                 tokens.Srcs[j].Length);
  426.                         src_str[tokens.Srcs[j].Length] = '\0';
  427.                         init_rc_normal_src(inst, j, src_str);
  428.                 }
  429.                 if (info->HasTexture) {
  430.                         /* XXX: Will this always be XYZW ? */
  431.                         inst->U.I.TexSwizzle = RC_SWIZZLE_XYZW;
  432.                 }
  433.                 break;
  434.         }
  435.         return 1;
  436. }
  437.  
  438. #define INDEX_TOKEN_LEN 4
  439. #define FLOAT_TOKEN_LEN 50
  440. int parse_constant(unsigned *index, float *data, const char *const_str)
  441. {
  442.         int matched = sscanf(const_str, "const[%d] {%f, %f, %f, %f}", index,
  443.                                 &data[0], &data[1], &data[2], &data[3]);
  444.         return matched == 5;
  445. }
  446.  
  447. int init_rc_normal_instruction(
  448.         struct rc_instruction * inst,
  449.         const char * inst_str)
  450. {
  451.         /* Initialize inst */
  452.         memset(inst, 0, sizeof(struct rc_instruction));
  453.  
  454.         return parse_rc_normal_instruction(inst, inst_str);
  455. }
  456.  
  457. void add_instruction(struct radeon_compiler *c, const char * inst_string)
  458. {
  459.         struct rc_instruction * new_inst =
  460.                 rc_insert_new_instruction(c, c->Program.Instructions.Prev);
  461.  
  462.         parse_rc_normal_instruction(new_inst, inst_string);
  463.  
  464. }
  465.  
  466. int add_constant(struct radeon_compiler *c, const char *const_str)
  467. {
  468.         float data[4];
  469.         unsigned index;
  470.         struct rc_constant_list *constants;
  471.         struct rc_constant constant;
  472.  
  473.         if (!parse_constant(&index, data, const_str)) {
  474.                 return 0;
  475.         }
  476.  
  477.         constants = &c->Program.Constants;
  478.         if (constants->_Reserved < index) {
  479.                 struct rc_constant * newlist;
  480.  
  481.                 constants->_Reserved = index + 100;
  482.  
  483.                 newlist = malloc(sizeof(struct rc_constant) * constants->_Reserved);
  484.                 if (constants->Constants) {
  485.                         memcpy(newlist, constants->Constants,
  486.                                 sizeof(struct rc_constant) *
  487.                                         constants->_Reserved);
  488.                         free(constants->Constants);
  489.                 }
  490.  
  491.                 constants->Constants = newlist;
  492.         }
  493.  
  494.         memset(&constant, 0, sizeof(constant));
  495.         constant.Type = RC_CONSTANT_IMMEDIATE;
  496.         constant.Size = 4;
  497.         memcpy(constant.u.Immediate, data, sizeof(float) * 4);
  498.         constants->Constants[index] = constant;
  499.         constants->Count = MAX2(constants->Count, index + 1);
  500.  
  501.         return 1;
  502. }
  503.  
  504. void init_compiler(
  505.         struct radeon_compiler *c,
  506.         enum rc_program_type program_type,
  507.         unsigned is_r500,
  508.         unsigned is_r400)
  509. {
  510.         struct rc_regalloc_state *rs = malloc(sizeof(struct rc_regalloc_state));
  511.         rc_init_regalloc_state(rs);
  512.         rc_init(c, rs);
  513.  
  514.         c->is_r500 = is_r500;
  515.         c->max_temp_regs = is_r500 ? 128 : (is_r400 ? 64 : 32);
  516.         c->max_constants = is_r500 ? 256 : 32;
  517.         c->max_alu_insts = (is_r500 || is_r400) ? 512 : 64;
  518.         c->max_tex_insts = (is_r500 || is_r400) ? 512 : 32;
  519.         if (program_type == RC_FRAGMENT_PROGRAM) {
  520.                 c->has_half_swizzles = 1;
  521.                 c->has_presub = 1;
  522.                 c->has_omod = 1;
  523.                 c->SwizzleCaps =
  524.                         is_r500 ? &r500_swizzle_caps : &r300_swizzle_caps;
  525.         } else {
  526.                 c->SwizzleCaps = &r300_vertprog_swizzle_caps;
  527.         }
  528. }
  529.  
  530. #define MAX_LINE_LENGTH 100
  531. #define MAX_PATH_LENGTH 100
  532.  
  533. unsigned load_program(
  534.         struct radeon_compiler *c,
  535.         struct rc_test_file *test,
  536.         const char *filename)
  537. {
  538.         char line[MAX_LINE_LENGTH];
  539.         char path[MAX_PATH_LENGTH];
  540.         FILE *file;
  541.         unsigned *count;
  542.         char **string_store;
  543.         unsigned i = 0;
  544.  
  545.         snprintf(path, MAX_PATH_LENGTH, "compiler/tests/%s", filename);
  546.         file = fopen(path, "r");
  547.         if (!file) {
  548.                 return 0;
  549.         }
  550.         memset(test, 0, sizeof(struct rc_test_file));
  551.  
  552.         count = &test->num_input_lines;
  553.  
  554.         while (fgets(line, MAX_LINE_LENGTH, file)){
  555.                 if (line[MAX_LINE_LENGTH - 2] == '\n') {
  556.                         fprintf(stderr, "Error line cannot be longer than 100 "
  557.                                 "characters:\n%s\n", line);
  558.                         return 0;
  559.                 }
  560.  
  561.                 // Comment
  562.                 if (line[0] == '#' || is_whitespace(line)) {
  563.                         continue;
  564.                 }
  565.  
  566.                 if (line[0] == '=') {
  567.                         count = &test->num_expected_lines;
  568.                         continue;
  569.                 }
  570.  
  571.                 (*count)++;
  572.         }
  573.  
  574.         test->input = malloc(sizeof(char *) * test->num_input_lines);
  575.         test->expected = malloc(sizeof(char *) * test->num_expected_lines);
  576.  
  577.         rewind(file);
  578.         string_store = test->input;
  579.  
  580.         while(fgets(line, MAX_LINE_LENGTH, file)) {
  581.                 // Comment
  582.                 char * dst;
  583.                 if (line[0] == '#' || is_whitespace(line)) {
  584.                         continue;
  585.                 }
  586.  
  587.                 if (line[0] == '=') {
  588.                         i = 0;
  589.                         string_store = test->expected;
  590.                         continue;
  591.                 }
  592.  
  593.                 dst = string_store[i++] = malloc((strlen(line) + 1) *
  594.                                                         sizeof (char));
  595.                 strcpy(dst, line);
  596.         }
  597.  
  598.         for (i = 0; i < test->num_input_lines; i++) {
  599.                 if (test->input[i][0] == 'c') {
  600.                         add_constant(c, test->input[i]);
  601.                         continue;
  602.                 }
  603.                 // XXX: Parse immediates from the file.
  604.                 add_instruction(c, test->input[i]);
  605.         }
  606.         return 1;
  607. }
  608.