view lwlink/output.c @ 418:3832a68d83ef

Fix internal compiler error on "var2 = var1 + 1" patterns This appears to be the correct fix. It was provided by Tormod Volden (debian.tormod@gmail.com). The final commit is substantially different from Tormod's submission mostly due to housecleaning (removing the old patches and updating the README). Tormod's comments follow. The original addhi_mem_1 "insn" instruction pattern /matches/ two memory operands, just with the /constraint/ that these are the same location. A pattern match tells the compiler "you should be able to use this, but you might have to work on it to meet the constraints". For typical constraints on registers the compiler can add "reloads", moving stuff between registers or from memory, until the constraints are met and the instruction can be used. However, in this case, no amount of reloads can make two memory locations the same if they already weren't, so the compiler breaks down and cries "unable to generate reloads". It seems this issue only appears if optimization is enabled. The proof is in gcc's reload.c and is left as an exercise to the reader. Limiting the matching pattern to identical memory operands avoids these situations, while allowing the common "var++" cases. References: The pattern/constraints difference is explained in https://gcc.gnu.org/onlinedocs/gccint/Simple-Constraints.html#index-other-register-constraints-3335
author William Astle <lost@l-w.ca>
date Tue, 29 Mar 2016 21:21:49 -0600
parents 55c1f9a321e9
children 9f0448022f1f
line wrap: on
line source

/*
output.c
Copyright © 2009 William Astle

This file is part of LWLINK.

LWLINK is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.

You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.


Actually output the binary
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "lwlink.h"

// this prevents warnings about not using the return value of fwrite()
// and, theoretically, can be replaced with a function that handles things
// better in the future
//#define writebytes(s, l, c, f)	do { int r; r = fwrite((s), (l), (c), (f)); (void)r; } while (0)
#define writebytes(s, l, c, f)	do { (void)(fwrite((s), (l), (c), (f)) && 1); } while (0)

void do_output_os9(FILE *of);
void do_output_decb(FILE *of);
void do_output_raw(FILE *of);
void do_output_lwex0(FILE *of);
void do_output_srec(FILE *of);

void do_output(void)
{
	FILE *of;
	
	of = fopen(outfile, "wb");
	if (!of)
	{
		fprintf(stderr, "Cannot open output file %s: ", outfile);
		perror("");
		exit(1);
	}
	
	switch (outformat)
	{
	case OUTPUT_DECB:
		do_output_decb(of);
		break;
	
	case OUTPUT_RAW:
		do_output_raw(of);
		break;

	case OUTPUT_LWEX0:
		do_output_lwex0(of);
		break;
	
	case OUTPUT_OS9:
		do_output_os9(of);
		break;
		
	case OUTPUT_SREC:
		do_output_srec(of);
		break;
		
	default:
		fprintf(stderr, "Unknown output format doing output!\n");
		exit(111);
	}
	
	fclose(of);
}

void do_output_decb(FILE *of)
{
	int sn, sn2;
	int cloc, olen;
	unsigned char buf[5];
	
	for (sn = 0; sn < nsects; sn++)
	{
		if (sectlist[sn].ptr -> flags & SECTION_BSS)
		{
			// no output for a BSS section
			continue;
		}
		if (sectlist[sn].ptr -> codesize == 0)
		{
			// don't generate output for a zero size section
			continue;
		}
		
		// calculate the length of this output block
		cloc = sectlist[sn].ptr -> loadaddress;
		olen = 0;
		for (sn2 = sn; sn2 < nsects; sn2++)
		{
			// ignore BSS sections
			if (sectlist[sn2].ptr -> flags & SECTION_BSS)
				continue;
			// ignore zero length sections
			if (sectlist[sn2].ptr -> codesize == 0)
				continue;
			if (cloc != sectlist[sn2].ptr -> loadaddress)
				break;
			olen += sectlist[sn2].ptr -> codesize;
			cloc += sectlist[sn2].ptr -> codesize;
		}
		
		// write a preamble
		buf[0] = 0x00;
		buf[1] = olen >> 8;
		buf[2] = olen & 0xff;
		buf[3] = sectlist[sn].ptr -> loadaddress >> 8;
		buf[4] = sectlist[sn].ptr -> loadaddress & 0xff;
		writebytes(buf, 1, 5, of);
		for (; sn < sn2; sn++)
		{
			if (sectlist[sn].ptr -> flags & SECTION_BSS)
				continue;
			if (sectlist[sn].ptr -> codesize == 0)
				continue;
			writebytes(sectlist[sn].ptr -> code, 1, sectlist[sn].ptr -> codesize, of);
		}
		sn--;
	}
	// write a postamble
	buf[0] = 0xff;
	buf[1] = 0x00;
	buf[2] = 0x00;
	buf[3] = linkscript.execaddr >> 8;
	buf[4] = linkscript.execaddr & 0xff;
	writebytes(buf, 1, 5, of);
}

void do_output_raw(FILE *of)
{
	int nskips = 0;		// used to output blanks for BSS inline
	int sn;
	
	for (sn = 0; sn < nsects; sn++)
	{
		if (sectlist[sn].ptr -> flags & SECTION_BSS)
		{
			// no output for a BSS section
			nskips += sectlist[sn].ptr -> codesize;
			continue;
		}
		while (nskips > 0)
		{
			// the "" is not an error - it turns into a single NUL byte!
			writebytes("", 1, 1, of);
			nskips--;
		}
		writebytes(sectlist[sn].ptr -> code, 1, sectlist[sn].ptr -> codesize, of);
	}
}

void do_output_srec(FILE *of)
{
	const int SRECLEN = 16;

	int sn;	
	int remainingcodebytes;
	
	int codeaddr;
	int i;
	int recaddr = 0;
	int recdlen = 0;
	int recsum;
	int reccnt = -1;
	unsigned char* sectcode;
	// no header yet; unnecessary

	for (sn = 0; sn < nsects; sn++)				// check all sections
	{
		if (sectlist[sn].ptr -> flags & SECTION_BSS)	// ignore BSS sections
			continue;
		if (sectlist[sn].ptr -> codesize == 0)		// ignore empty sections
			continue;

		recaddr = sectlist[sn].ptr -> loadaddress;
		remainingcodebytes = sectlist[sn].ptr -> codesize;
		sectcode = sectlist[sn].ptr -> code;
		
		while (remainingcodebytes) 
		{
			recdlen = (SRECLEN>remainingcodebytes)?remainingcodebytes:SRECLEN;
			recsum = recdlen + 3;
			codeaddr = recaddr - sectlist[sn].ptr -> loadaddress;			
			fprintf(of, "S1%02X%04X", recdlen + 3, recaddr);
			for (i = 0; i < recdlen; i++)
			{
				fprintf(of, "%02X", sectcode[codeaddr+i]);
				recsum += sectcode[codeaddr+i];
			}
			recsum += (recaddr >> 8) & 0xFF;
			recsum += recaddr & 0xFF;
			fprintf(of, "%02X\r\n", (unsigned char)(~recsum));
			reccnt += 1;
			remainingcodebytes -= recdlen;
			recaddr += recdlen;
		}
	}
	// S9 record as a footer to inform about start addr
	recsum = 3;
	recsum += (linkscript.execaddr >> 8) & 0xFF;
	recsum += linkscript.execaddr & 0xFF;
	fprintf(of,"S903%04X%02X\r\n",linkscript.execaddr,(unsigned char)(~recsum));
}

void do_output_lwex0(FILE *of)
{
	int nskips = 0;		// used to output blanks for BSS inline
	int sn;
	int codedatasize = 0;
	unsigned char buf[32];
	
	// calculate items for the file header
	for (sn = 0; sn < nsects; sn++)
	{
		if (sectlist[sn].ptr -> flags & SECTION_BSS)
		{
			// no output for a BSS section
			nskips += sectlist[sn].ptr -> codesize;
			continue;
		}
		codedatasize += nskips;
		nskips = 0;
		codedatasize += sectlist[sn].ptr -> codesize;
	}

	// output the file header
	buf[0] = 'L';
	buf[1] = 'W';
	buf[2] = 'E';
	buf[3] = 'X';
	buf[4] = 0;		// version 0
	buf[5] = 0;		// low stack
	buf[6] = linkscript.stacksize / 256;
	buf[7] = linkscript.stacksize & 0xff;
	buf[8] = nskips / 256;
	buf[9] = nskips & 0xff;
	buf[10] = codedatasize / 256;
	buf[11] = codedatasize & 0xff;
	buf[12] = linkscript.execaddr / 256;
	buf[13] = linkscript.execaddr & 0xff;
	memset(buf + 14, 0, 18);
	
	writebytes(buf, 1, 32, of);
	// output the data
	// NOTE: disjoint load addresses will not work correctly!!!!!
	nskips = 0;
	for (sn = 0; sn < nsects; sn++)
	{
		if (sectlist[sn].ptr -> flags & SECTION_BSS)
		{
			// no output for a BSS section
			nskips += sectlist[sn].ptr -> codesize;
			continue;
		}
		while (nskips > 0)
		{
			// the "" is not an error - it turns into a single NUL byte!
			writebytes("", 1, 1, of);
			nskips--;
		}
		writebytes(sectlist[sn].ptr -> code, 1, sectlist[sn].ptr -> codesize, of);
	}
}

void os9crc(unsigned char crc[3], unsigned char b)
{
	b ^= crc[0];
	crc[0] = crc[1];
	crc[1] = crc[2];
	crc[1] ^= b >> 7;
	crc[2] = b << 1;
	crc[1] ^= b >> 2;
	crc[2] ^= b << 6;
	b ^= b << 1;
	b ^= b << 2;
	b ^= b << 4;
	if (b & 0x80)
	{
		crc[0] ^= 0x80;
		crc[2] ^= 0x21;
	}
}
 

void do_output_os9(FILE *of)
{
	int sn;
	int codedatasize = 0;
	int bsssize = 0;
	int nameoff;
	int i;
		
	unsigned char buf[16];
	unsigned char crc[3];
	
	// calculate items for the file header
	for (sn = 0; sn < nsects; sn++)
	{
		if (sectlist[sn].ptr -> flags & SECTION_BSS)
		{
			// no output for a BSS section
			bsssize += sectlist[sn].ptr -> codesize;
			continue;
		}
		codedatasize += sectlist[sn].ptr -> codesize;
	}
	bsssize += linkscript.stacksize;

	// now bss size is the data size for the module
	// and codesize is the length of the module minus the module header
	// and CRC

	codedatasize += 13; // add in headers
	nameoff = codedatasize; // we'll put the name at the end
	codedatasize += 3;	// add in CRC
	codedatasize += strlen(linkscript.name); // add in name length
	if (linkscript.edition >= 0)
		codedatasize += 1;
	
	// output the file header
	buf[0] = 0x87;
	buf[1] = 0xCD;
	buf[2] = (codedatasize >> 8) & 0xff;
	buf[3] = codedatasize & 0xff;
	buf[4] = (nameoff >> 8) & 0xff;
	buf[5] = nameoff & 0xff;
	buf[6] = (linkscript.modtype << 4) | (linkscript.modlang);
	buf[7] = (linkscript.modattr << 4) | (linkscript.modrev);
	buf[8] = (~(buf[0] ^ buf[1] ^ buf[2] ^ buf[3] ^ buf[4] ^ buf[5] ^ buf[6] ^ buf[7])) & 0xff;
	buf[9] = (linkscript.execaddr >> 8) & 0xff;
	buf[10] = linkscript.execaddr & 0xff;
	buf[11] = (bsssize >> 8) & 0xff;
	buf[12] = bsssize & 0xff;
	
	crc[0] = 0xff;
	crc[1] = 0xff;
	crc[2] = 0xff;
	
	os9crc(crc, buf[0]);
	os9crc(crc, buf[1]);
	os9crc(crc, buf[2]);
	os9crc(crc, buf[3]);
	os9crc(crc, buf[4]);
	os9crc(crc, buf[5]);
	os9crc(crc, buf[6]);
	os9crc(crc, buf[7]);
	os9crc(crc, buf[8]);
	os9crc(crc, buf[9]);
	os9crc(crc, buf[10]);
	os9crc(crc, buf[11]);
	os9crc(crc, buf[12]);
	
	
	writebytes(buf, 1, 13, of);
	
	// output the data
	// NOTE: disjoint load addresses will not work correctly!!!!!
	for (sn = 0; sn < nsects; sn++)
	{
		if (sectlist[sn].ptr -> flags & SECTION_BSS)
		{
			// no output for a BSS section
			continue;
		}
		writebytes(sectlist[sn].ptr -> code, 1, sectlist[sn].ptr -> codesize, of);
		for (i = 0; i < sectlist[sn].ptr -> codesize; i++)
			os9crc(crc, sectlist[sn].ptr -> code[i]);
	}
	
	// output the name
	for (i = 0; linkscript.name[i + 1]; i++)
	{
		writebytes(linkscript.name + i, 1, 1, of);
		os9crc(crc, linkscript.name[i]);
	}
	buf[0] = linkscript.name[i] | 0x80;
	writebytes(buf, 1, 1, of);
	os9crc(crc, buf[0]);
	
	if (linkscript.edition >= 0)
	{
		buf[0] = linkscript.edition & 0xff;
		writebytes(buf, 1, 1, of);
		os9crc(crc, buf[0]);
	}
	
	crc[0] ^= 0xff;
	crc[1] ^= 0xff;
	crc[2] ^= 0xff;
	writebytes(crc, 1, 3, of);
}