Subversion Repositories Kolibri OS

Rev

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