Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright 2011 Tom Stellard <tstellar (at) 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 (at) 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