Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
163dcbd961 |
18
Makefile
18
Makefile
@@ -14,7 +14,6 @@
|
||||
MAX32 = pic32/max32
|
||||
UBW32 = pic32/ubw32
|
||||
UBW32UART = pic32/ubw32-uart
|
||||
UBW32UARTSDRSW = pic32/ubw32-uart-sdramswap
|
||||
MAXIMITE = pic32/maximite
|
||||
EXPLORER16 = pic32/explorer16
|
||||
STARTERKIT = pic32/starter-kit
|
||||
@@ -25,7 +24,7 @@ BAREMETAL = pic32/baremetal
|
||||
RETROONE = pic32/retroone
|
||||
|
||||
# Select target board
|
||||
TARGET ?= $(UBW32UARTSDRSW)
|
||||
TARGET ?= $(MAX32)
|
||||
|
||||
# Filesystem and swap sizes.
|
||||
FS_KBYTES = 16384
|
||||
@@ -50,6 +49,7 @@ FSUTIL = tools/fsutil/fsutil
|
||||
#
|
||||
BIN_FILES := $(wildcard bin/*)
|
||||
SBIN_FILES := $(wildcard sbin/*)
|
||||
UFLASH_FILES := $(wildcard uflash/*)
|
||||
GAMES_FILES := $(shell find games -type f ! -path '*/.*')
|
||||
LIB_FILES := $(wildcard lib/*)
|
||||
LIBEXEC_FILES := $(wildcard libexec/*)
|
||||
@@ -62,13 +62,16 @@ SHARE_FILES = share/re.help share/example/Makefile \
|
||||
share/example/ashello.S share/example/chello.c \
|
||||
share/example/blkjack.bas share/example/hilow.bas \
|
||||
share/example/stars.bas share/example/prime.scm \
|
||||
share/example/fact.fth share/example/echo.S
|
||||
share/example/fact.fth share/example/echo.S \
|
||||
share/smallc/lib.c share/smallc/Makefile share/smallc/primelist.c \
|
||||
share/smallc/primesum.c share/smallc/sys.s share/smallc/test1.c \
|
||||
share/smallc/test2.c $(wildcard share/lccom/*)
|
||||
ALLFILES = $(SBIN_FILES) $(ETC_FILES) $(BIN_FILES) $(LIB_FILES) $(LIBEXEC_FILES) \
|
||||
$(INC_FILES) $(SHARE_FILES) $(GAMES_FILES) \
|
||||
$(INC_FILES) $(SHARE_FILES) $(GAMES_FILES) $(UFLASH_FILES) \
|
||||
var/log/messages var/log/wtmp .profile
|
||||
ALLDIRS = sbin/ bin/ dev/ etc/ tmp/ lib/ libexec/ share/ share/example/ \
|
||||
share/misc/ var/ var/run/ var/log/ u/ include/ include/sys/ \
|
||||
games/ games/lib/
|
||||
share/lccom/ share/misc/ share/smallc/ var/ var/run/ var/log/ u/ include/ include/sys/ \
|
||||
games/ games/lib/ uflash/
|
||||
BDEVS = dev/sd0!b0:0 dev/sd1!b0:1 dev/sw0!b1:0
|
||||
CDEVS = dev/console!c0:0 \
|
||||
dev/mem!c1:0 dev/kmem!c1:1 dev/null!c1:2 dev/zero!c1:3 \
|
||||
@@ -82,7 +85,7 @@ CDEVS = dev/console!c0:0 \
|
||||
dev/confa!c7:64 dev/confb!c7:65 dev/confc!c7:66 \
|
||||
dev/confd!c7:67 dev/confe!c7:68 dev/conff!c7:69 dev/confg!c7:70 \
|
||||
dev/spi1!c9:0 dev/spi2!c9:1 dev/spi3!c9:2 dev/spi4!c9:3 \
|
||||
dev/glcd0!c10:0
|
||||
dev/glcd0!c10:0 dev/uflash!c12:0
|
||||
FDDEVS = dev/fd/ dev/fd/0!c5:0 dev/fd/1!c5:1 dev/fd/2!c5:2 \
|
||||
dev/fd/3!c5:3 dev/fd/4!c5:4 dev/fd/5!c5:5 dev/fd/6!c5:6 \
|
||||
dev/fd/7!c5:7 dev/fd/8!c5:8 dev/fd/9!c5:9 dev/fd/10!c5:10 \
|
||||
@@ -139,6 +142,7 @@ cleanall: clean
|
||||
rm -f share/re.help
|
||||
rm -f share/misc/more.help
|
||||
rm -f etc/termcap
|
||||
rm -f uflash/*
|
||||
|
||||
|
||||
# TODO
|
||||
|
||||
755
sdram-fpga.S
755
sdram-fpga.S
@@ -1,755 +0,0 @@
|
||||
/*
|
||||
* SDRAM Access Routines for PIC32.
|
||||
*
|
||||
* Retromaster - 10.05.2010
|
||||
*
|
||||
* This file is in the public domain. You can use, modify, and distribute the source code
|
||||
* and executable programs based on the source code. This file is provided "as is" and
|
||||
* without any express or implied warranties whatsoever. Use at your own risk!
|
||||
*
|
||||
* Changes by jmcgee for inclusion in the retrobsd project.
|
||||
*/
|
||||
|
||||
/* SDRAM Used: HY57V281620
|
||||
* SDRAM Pin to PIC32 Pin Mapping:
|
||||
* SDRAM PIC32
|
||||
* ------ ------
|
||||
* note: the order of An to RBn DOES matter
|
||||
* At the moment it is a bit of a mess and
|
||||
* needs to be cleaned up.
|
||||
* A0 23 RB11
|
||||
* A1 24 RB12
|
||||
* A2 25 RB13
|
||||
* A3 26 RB14
|
||||
* A4 29 RB5
|
||||
* A5 30 RB4
|
||||
* A6 31 RB3
|
||||
* A7 32 RB2
|
||||
* A8 33 RB6
|
||||
* A9 34 RB7
|
||||
* A10 22 RB15
|
||||
* A11 35 RB9
|
||||
*
|
||||
* BA0 20 RD4
|
||||
* BA1 21 RD5
|
||||
*
|
||||
* note: the order of DQn to RAn does not matter
|
||||
* DQ0 2 RA6
|
||||
* DQ1 4 RA1
|
||||
* DQ2 5 RA7
|
||||
* DQ3 7 RA2
|
||||
* DQ4 8 RA3
|
||||
* DQ5 10 RA4
|
||||
* DQ6 11 RA5
|
||||
* DQ7 13 RA0
|
||||
|
||||
* CLK 38 OC1-RD0
|
||||
* CKE 37 A10
|
||||
* CS 19 RF12
|
||||
* WE 16 RF0
|
||||
* CAS 17 RF1
|
||||
* RAS 18 RF13
|
||||
*
|
||||
* SDRam 42, 44, 45, 47, 48, 50, 51, 53 - (d8-d15) should be pulled up, but
|
||||
* on the prototype board these were simply left floating
|
||||
*
|
||||
* Power connections not listed.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Retrobsd does not currently contain a good header
|
||||
* for pulling the pic32 port addresses into an assembly
|
||||
* file, so define some needed registers here for the moment.
|
||||
*/
|
||||
|
||||
#define TRISA 0xBF886000
|
||||
#define T2CON 0xBF800800
|
||||
#define TMR2 0xBF800810
|
||||
#define PR2 0xBF800820
|
||||
#define T2CONSET 0xBF800808
|
||||
|
||||
#define OC1CON 0xBF803000
|
||||
#define OC4CON 0xBF803600
|
||||
#define OCXR_OFFSET 0x10
|
||||
#define OCXRS_OFFSET 0x20
|
||||
|
||||
#define AD1PCFGSET 0xBF809068
|
||||
|
||||
/* Offsets (from TRISA) for the io ports */
|
||||
#define SDR_OFFSET_A 0
|
||||
#define SDR_OFFSET_B 0x40
|
||||
#define SDR_OFFSET_C 0x80
|
||||
#define SDR_OFFSET_D 0xc0
|
||||
#define SDR_OFFSET_E 0x100
|
||||
#define SDR_OFFSET_F 0x140
|
||||
#define SDR_OFFSET_G 0x180
|
||||
|
||||
/* Offsets (from TRISA) for the various port control registers */
|
||||
#define TRIS_OFFSET 0x0
|
||||
#define PORT_OFFSET 0x10
|
||||
#define LAT_OFFSET 0x20
|
||||
#define ODCF_OFFSET 0x30
|
||||
|
||||
/* Offsets (from TRISA) for the various io port bit manipulator registers */
|
||||
#define NOP_OP_OFFSET 0x0
|
||||
#define CLR_OP_OFFSET 0x4
|
||||
#define SET_OP_OFFSET 0x8
|
||||
#define INV_OP_OFFSET 0xc
|
||||
|
||||
|
||||
/*
|
||||
* Specific assignments of ports ports used
|
||||
* Note: In general, it is not sufficient to
|
||||
* change the constants below - corresponding
|
||||
* changes to the code below will likely be
|
||||
* required if any of the following values
|
||||
* are changed.
|
||||
*/
|
||||
|
||||
/* DATA_PORT_TRIS must be assigned to a TRIS of the port
|
||||
* that has the low 8 bits tied the 8 data bits on the ram.
|
||||
*/
|
||||
#define SDR_OCR OC1CON
|
||||
|
||||
#define SDR_DATA_IO SDR_OFFSET_A
|
||||
#define SDR_DATA_TRIS SDR_OFFSET_A
|
||||
|
||||
#define SDR_ADDRESS_IO SDR_OFFSET_B
|
||||
#define SDR_ADDRESS_TRIS SDR_OFFSET_B
|
||||
#define ADDRESS_MASK 0xfafc
|
||||
|
||||
#define SDR_BANK_IO SDR_OFFSET_D
|
||||
#define SDR_BANK_TRIS SDR_OFFSET_D
|
||||
#define BANK_0_BIT 4
|
||||
#define BANK_1_BIT 5
|
||||
|
||||
#define SDR_CONTROL_IO SDR_OFFSET_F
|
||||
#define SDR_CONTROL_TRIS SDR_OFFSET_F
|
||||
#define CONTROL_WE_BIT 0
|
||||
#define CONTROL_CAS_BIT 1
|
||||
#define CONTROL_CS_BIT 12
|
||||
#define CONTROL_RAS_BIT 13
|
||||
|
||||
#define SDR_CKE_IO SDR_OFFSET_A
|
||||
#define SDR_CKE_TRIS SDR_OFFSET_A
|
||||
#define CKE_BIT 10
|
||||
#define DATA_DIR_BIT 15
|
||||
|
||||
#define CONTROL_ALL_MASK ( (1<<CONTROL_CS_BIT) | (1<<CONTROL_RAS_BIT) | (1<<CONTROL_CAS_BIT) | (1<<CONTROL_WE_BIT) )
|
||||
#define BANK_ALL_MASK ( (1 << BANK_1_BIT) | ( 1 << BANK_0_BIT ) )
|
||||
|
||||
/* Global Symbols */
|
||||
.globl sdram_init
|
||||
.globl sdram_read
|
||||
.globl sdram_write
|
||||
.globl sdram_active
|
||||
.globl sdram_auto_refresh
|
||||
.globl sdram_precharge
|
||||
.globl sdram_precharge_all
|
||||
.globl sdram_sleep
|
||||
.globl sdram_wake
|
||||
.globl sdram_bank
|
||||
|
||||
.type sdram_init, @function
|
||||
.type sdram_read, @function
|
||||
.type sdram_write, @function
|
||||
.type sdram_active, @function
|
||||
.type sdram_auto_refresh, @function
|
||||
.type sdram_precharge, @function
|
||||
.type sdram_precharge_all, @function
|
||||
.type sdram_sleep, @function
|
||||
.type sdram_wake, @function
|
||||
.type sdram_bank, @function
|
||||
|
||||
/*
|
||||
* This code MUST execute from ram and the ram MUST be configured
|
||||
* for zero wait states. Interrupts MUST disabled before
|
||||
* calling any of these functions, and any DMA MUST also be
|
||||
* disabled.
|
||||
*
|
||||
* Also, the peripheral bus divisor must be set to 1.
|
||||
*/
|
||||
|
||||
.section .ramfunc,"ax",@progbits
|
||||
|
||||
/* No instruction reordering */
|
||||
|
||||
.set noreorder
|
||||
|
||||
#define clock4 \
|
||||
nop;nop;nop;nop
|
||||
|
||||
#define clock3 \
|
||||
nop;nop;nop
|
||||
|
||||
#define clock2 \
|
||||
nop;nop
|
||||
|
||||
#define clock1 \
|
||||
nop
|
||||
|
||||
/*
|
||||
* The SDRAM clock is output from the output compare unit.
|
||||
* This macro synchronizes with that clock so that we are
|
||||
* sure to have at least two clock cycles to issue control
|
||||
* line changes and access the data bus before the rising
|
||||
* edge.
|
||||
*/
|
||||
|
||||
|
||||
#define sync_clock \
|
||||
la $t8, TMR2; \
|
||||
li $v0, 2; \
|
||||
lw $v1, ($t8); \
|
||||
bge $v1, $v0, 1f; \
|
||||
nop; \
|
||||
nop; \
|
||||
nop; \
|
||||
1: \
|
||||
nop;
|
||||
|
||||
#define output_column \
|
||||
and $t5, $a0, 0x1f; \
|
||||
sll $t5, 2; \
|
||||
and $t6, $a0, 0x20; \
|
||||
sll $t6, 9; \
|
||||
or $t5, $t6; \
|
||||
lw $t6, SDR_ADDRESS_TRIS + LAT_OFFSET($t0); \
|
||||
and $t6, ~ADDRESS_MASK; \
|
||||
or $t6, $t5; \
|
||||
sw $t6, SDR_ADDRESS_TRIS + LAT_OFFSET($t0);
|
||||
|
||||
#define output_address \
|
||||
sll $t5, $a0, 2; \
|
||||
and $t5, ADDRESS_MASK; \
|
||||
and $t6, $a0, 0x40; \
|
||||
sll $t6, 8; \
|
||||
or $t5, $t6; \
|
||||
and $t6, $a0, 0x100; \
|
||||
sll $t6, 7; \
|
||||
or $t5, $t6; \
|
||||
lw $t6, SDR_ADDRESS_TRIS + LAT_OFFSET($t0); \
|
||||
and $t6, ~ADDRESS_MASK; \
|
||||
or $t6, $t5; \
|
||||
sw $t6, SDR_ADDRESS_TRIS + LAT_OFFSET($t0);
|
||||
|
||||
/*.ent sdram_init*/
|
||||
|
||||
/*
|
||||
* Initializes the SDRAM.
|
||||
* Should be called once sometime after startup
|
||||
* C Prototype:
|
||||
* extern __attribute__((far)) void sdram_init();
|
||||
*/
|
||||
|
||||
sdram_init:
|
||||
|
||||
/* Initialize address lines */
|
||||
la $t0, TRISA /* base of io addresses */
|
||||
li $t1, ADDRESS_MASK
|
||||
sw $t1, SDR_ADDRESS_IO + TRIS_OFFSET + CLR_OP_OFFSET($t0)
|
||||
|
||||
li $t1, 0xFFFF
|
||||
sw $t1, AD1PCFGSET
|
||||
|
||||
li $t1, BANK_ALL_MASK
|
||||
sw $t1, SDR_BANK_IO + TRIS_OFFSET + CLR_OP_OFFSET($t0)
|
||||
|
||||
/* All address lines low */
|
||||
li $t1, ADDRESS_MASK
|
||||
sw $t1, SDR_ADDRESS_IO + LAT_OFFSET + CLR_OP_OFFSET($t0)
|
||||
|
||||
li $t1, BANK_ALL_MASK
|
||||
sw $t1, SDR_BANK_IO + LAT_OFFSET + CLR_OP_OFFSET($t0)
|
||||
|
||||
/* Initialize data lines */
|
||||
li $t1, 0xFF
|
||||
sw $t1, SDR_DATA_IO + TRIS_OFFSET + SET_OP_OFFSET($t0)
|
||||
|
||||
/* Initialize SDRAM control lines */
|
||||
li $t1, CONTROL_ALL_MASK
|
||||
sw $t1, SDR_CONTROL_IO + TRIS_OFFSET + CLR_OP_OFFSET($t0)
|
||||
|
||||
/* Command Inhibit */
|
||||
li $t1, CONTROL_ALL_MASK
|
||||
sw $t1, SDR_CONTROL_IO + LAT_OFFSET + SET_OP_OFFSET($t0)
|
||||
|
||||
li $t1, (1<<DATA_DIR_BIT)
|
||||
sw $t1, SDR_CKE_IO + LAT_OFFSET + SET_OP_OFFSET($t0)
|
||||
|
||||
/* Initialize CKE line */
|
||||
// li $t1, (1<<CKE_BIT)
|
||||
li $t1, (1<<CKE_BIT)|(1<<DATA_DIR_BIT)
|
||||
sw $t1, SDR_CKE_IO + TRIS_OFFSET + CLR_OP_OFFSET($t0)
|
||||
|
||||
/* CKE low */
|
||||
li $t1, (1<<CKE_BIT)
|
||||
sw $t1, SDR_CKE_IO + LAT_OFFSET + CLR_OP_OFFSET($t0)
|
||||
|
||||
/* SDRAM clock output */
|
||||
|
||||
/* Initialize Timer2 */
|
||||
sw $zero, T2CON
|
||||
sw $zero, TMR2
|
||||
li $t1, 3
|
||||
sw $t1, PR2
|
||||
li $t1, 0x8000
|
||||
sw $t1, T2CONSET
|
||||
|
||||
/* Initialize OC device */
|
||||
sw $zero, SDR_OCR /*OC4CON */
|
||||
li $t1, 1
|
||||
sw $t1, SDR_OCR + OCXRS_OFFSET /*OC4RS*/
|
||||
li $t1, 3
|
||||
sw $t1, SDR_OCR + OCXR_OFFSET /*OC4R*/
|
||||
li $t1, 0x8005
|
||||
sw $t1, SDR_OCR /*OC4CON*/
|
||||
|
||||
/* Clock output starts here */
|
||||
|
||||
/* SD-RAM initialization delay */
|
||||
li $t2, 500
|
||||
move $t1, $zero
|
||||
|
||||
sdram_init_delay_1:
|
||||
addi $t1, $t1, 1
|
||||
bne $t1, $t2, sdram_init_delay_1
|
||||
nop
|
||||
|
||||
/* CKE high */
|
||||
li $t1, (1<<CKE_BIT)
|
||||
sw $t1, SDR_CKE_IO + LAT_OFFSET + SET_OP_OFFSET($t0)
|
||||
|
||||
/* Delay some more */
|
||||
li $t2, 3000
|
||||
move $t1, $zero
|
||||
|
||||
sdram_init_delay_2:
|
||||
addi $t1, $t1, 1
|
||||
bne $t1, $t2, sdram_init_delay_2
|
||||
nop
|
||||
|
||||
/* Get ready for the commands we are about to issue. */
|
||||
li $t4, (1<<CONTROL_CAS_BIT)
|
||||
li $t5, (1<<CONTROL_WE_BIT)
|
||||
li $t6, 0x1810 /* Mode Register: CL:2, BL:8 (0x23) */
|
||||
li $t7, CONTROL_ALL_MASK
|
||||
li $t8, 0x8000 /* A10 */
|
||||
|
||||
sw $t8, SDR_ADDRESS_IO + LAT_OFFSET + SET_OP_OFFSET($t0) /* A10 = 1 for Precharge ALL */
|
||||
|
||||
sync_clock
|
||||
|
||||
.set nomacro
|
||||
|
||||
/* Precharge All */
|
||||
sw $t7, SDR_CONTROL_IO + LAT_OFFSET + CLR_OP_OFFSET($t0) /* LLLL */
|
||||
sw $t4, SDR_CONTROL_IO + LAT_OFFSET + SET_OP_OFFSET($t0) /* LLHL */
|
||||
clock2
|
||||
|
||||
/* Auto Refresh 1 */
|
||||
sw $t4, SDR_CONTROL_IO + LAT_OFFSET + CLR_OP_OFFSET($t0) /* LLLL */
|
||||
sw $t5, SDR_CONTROL_IO + LAT_OFFSET + SET_OP_OFFSET($t0) /* LLLH */
|
||||
clock2
|
||||
|
||||
/* Auto Refresh 2 */
|
||||
clock4
|
||||
|
||||
/* Auto Refresh 3 */
|
||||
clock4
|
||||
|
||||
/* Auto Refresh 4 */
|
||||
clock4
|
||||
|
||||
/* Auto Refresh 5 */
|
||||
clock4
|
||||
|
||||
/* Auto Refresh 6 */
|
||||
clock4
|
||||
|
||||
/* Auto Refresh 7 */
|
||||
clock4
|
||||
|
||||
/* Auto Refresh 8 */
|
||||
li $t4, ADDRESS_MASK
|
||||
sw $t4, SDR_ADDRESS_IO + LAT_OFFSET + CLR_OP_OFFSET($t0)
|
||||
clock2
|
||||
|
||||
/* Load Mode Register */
|
||||
sw $t6, SDR_ADDRESS_IO + LAT_OFFSET + SET_OP_OFFSET($t0)
|
||||
sw $t7, SDR_CONTROL_IO + LAT_OFFSET + CLR_OP_OFFSET($t0)
|
||||
clock2
|
||||
|
||||
/* Command Inhibit */
|
||||
sw $t7, SDR_CONTROL_IO + LAT_OFFSET + SET_OP_OFFSET($t0)
|
||||
clock3
|
||||
|
||||
/* Command Inhibit */
|
||||
clock4
|
||||
|
||||
.set macro
|
||||
|
||||
jr $ra
|
||||
nop
|
||||
|
||||
/*.end sdram_init*/
|
||||
|
||||
|
||||
/*
|
||||
* Sends ACTIVE command
|
||||
* C Prototype:
|
||||
* extern __attribute__((far)) void sdram_active(uint16_t rowaddr);
|
||||
*/
|
||||
sdram_active:
|
||||
|
||||
la $t0, TRISA /* Port Base */
|
||||
li $t7, (1<<CONTROL_CS_BIT)|(1<<CONTROL_RAS_BIT)
|
||||
|
||||
/* Set row */
|
||||
output_address
|
||||
|
||||
sync_clock
|
||||
|
||||
.set nomacro
|
||||
|
||||
/* Active */
|
||||
sw $t7, SDR_CONTROL_IO + LAT_OFFSET + INV_OP_OFFSET($t0)
|
||||
clock3
|
||||
|
||||
/* Command Inhibit */
|
||||
sw $t7, SDR_CONTROL_IO + LAT_OFFSET + INV_OP_OFFSET($t0)
|
||||
clock3
|
||||
|
||||
/* Command Inhibit */
|
||||
clock4
|
||||
|
||||
.set macro
|
||||
|
||||
jr $ra
|
||||
nop
|
||||
|
||||
|
||||
/*
|
||||
* Sends WRITE command
|
||||
* C Prototype:
|
||||
* extern __attribute__((far)) void sdram_write(uint16_t pseudocoladdr, uint64_t val);
|
||||
* Each pseudo column contains 8 bytes of data (consists of 8 ram columns)
|
||||
*/
|
||||
|
||||
sdram_write:
|
||||
|
||||
la $t0, TRISA /* Port Base */
|
||||
li $t4, 0xFF
|
||||
li $t7, (1<<CONTROL_CS_BIT) | (1<<CONTROL_CAS_BIT) | (1<<CONTROL_WE_BIT)
|
||||
li $t3, (1<<DATA_DIR_BIT)
|
||||
|
||||
/* Set column */
|
||||
output_column
|
||||
|
||||
/* Set data lines */
|
||||
srl $t5, $a2, 24
|
||||
sb $t5, SDR_DATA_IO + LAT_OFFSET + NOP_OP_OFFSET($t0)
|
||||
|
||||
sw $t3, SDR_CKE_IO + LAT_OFFSET + INV_OP_OFFSET($t0)
|
||||
|
||||
sync_clock
|
||||
|
||||
.set nomacro
|
||||
|
||||
/* Write */
|
||||
sw $t7, SDR_CONTROL_IO + LAT_OFFSET + INV_OP_OFFSET($t0) /* LHLL */
|
||||
sw $t4, SDR_DATA_IO + TRIS_OFFSET + INV_OP_OFFSET($t0) /* 1 - enable data lines */
|
||||
srl $t5, $a2, 16
|
||||
clock1
|
||||
|
||||
/* Command Inhibit */
|
||||
sw $t7, SDR_CONTROL_IO + LAT_OFFSET + INV_OP_OFFSET($t0) /* HHHH */
|
||||
sb $t5, SDR_DATA_IO + LAT_OFFSET + NOP_OP_OFFSET($t0) /* 2 */
|
||||
srl $t5, $a2, 8
|
||||
clock1
|
||||
|
||||
/* Command Inhibit */
|
||||
sb $t5, SDR_DATA_IO + LAT_OFFSET + NOP_OP_OFFSET($t0) /* 3 */
|
||||
clock3
|
||||
|
||||
/* Command Inhibit */
|
||||
sb $a2, SDR_DATA_IO + LAT_OFFSET + NOP_OP_OFFSET($t0) /* 4 */
|
||||
srl $t5, $a3, 24
|
||||
clock2
|
||||
|
||||
/* Command Inhibit */
|
||||
sb $t5, SDR_DATA_IO + LAT_OFFSET + NOP_OP_OFFSET($t0) /* 5 */
|
||||
srl $t5, $a3, 16
|
||||
clock2
|
||||
|
||||
/* Command Inhibit */
|
||||
sb $t5, SDR_DATA_IO + LAT_OFFSET + NOP_OP_OFFSET($t0) /* 6 */
|
||||
srl $t5, $a3, 8
|
||||
clock2
|
||||
|
||||
/* Command Inhibit */
|
||||
sb $t5, SDR_DATA_IO + LAT_OFFSET + NOP_OP_OFFSET($t0) /* 7 */
|
||||
clock3
|
||||
|
||||
/* Command Inhibit */
|
||||
sb $a3, SDR_DATA_IO + LAT_OFFSET + NOP_OP_OFFSET($t0) /* 8 */
|
||||
clock3
|
||||
|
||||
sw $t4, SDR_DATA_IO + TRIS_OFFSET + INV_OP_OFFSET($t0) /* Data lines input again */
|
||||
|
||||
clock4 /* make certain sdram output has had time to disable - probably not needed */
|
||||
sw $t3, SDR_CKE_IO + LAT_OFFSET + INV_OP_OFFSET($t0)
|
||||
.set macro
|
||||
|
||||
jr $ra
|
||||
nop
|
||||
|
||||
|
||||
/*
|
||||
* Sends READ command
|
||||
* C Prototype:
|
||||
* extern __attribute__((far)) uint64_t sdram_read(uint16_t pseudocoladdr);
|
||||
* Each pseudo column contains 8 bytes of data (consists of 8 ram columns)
|
||||
*/
|
||||
sdram_read:
|
||||
|
||||
la $t0, TRISA /* Port Base */
|
||||
li $t7, (1<<CONTROL_CS_BIT) | (1<<CONTROL_CAS_BIT)
|
||||
|
||||
/* Set column */
|
||||
output_column
|
||||
|
||||
sync_clock
|
||||
|
||||
.set nomacro
|
||||
|
||||
/* Read */
|
||||
sw $t7, SDR_CONTROL_IO + LAT_OFFSET + INV_OP_OFFSET($t0) /* LHLH */
|
||||
clock3
|
||||
|
||||
/* Command Inhibit */
|
||||
sw $t7, SDR_CONTROL_IO + LAT_OFFSET + INV_OP_OFFSET($t0) /* HHHH */
|
||||
clock3
|
||||
|
||||
/* Command Inhibit */
|
||||
clock3
|
||||
lbu $v0, SDR_DATA_IO + PORT_OFFSET + NOP_OP_OFFSET($t0) /* 1 */
|
||||
|
||||
/* Command Inhibit */
|
||||
clock3
|
||||
lbu $t5, SDR_DATA_IO + PORT_OFFSET + NOP_OP_OFFSET($t0) /* 2 */
|
||||
|
||||
/* Command Inhibit */
|
||||
sll $v0, $v0, 8
|
||||
or $v0, $v0, $t5
|
||||
clock1
|
||||
lbu $t5, SDR_DATA_IO + PORT_OFFSET + NOP_OP_OFFSET($t0) /* 3 */
|
||||
|
||||
/* Command Inhibit */
|
||||
sll $v0, $v0, 8
|
||||
or $v0, $v0, $t5
|
||||
clock1
|
||||
lbu $t5, SDR_DATA_IO + PORT_OFFSET + NOP_OP_OFFSET($t0) /* 4 */
|
||||
|
||||
/* Command Inhibit */
|
||||
sll $v0, $v0, 8
|
||||
or $v0, $v0, $t5
|
||||
clock1
|
||||
lbu $v1, SDR_DATA_IO + PORT_OFFSET + NOP_OP_OFFSET($t0) /* 5 */
|
||||
|
||||
/* Command Inhibit */
|
||||
clock3
|
||||
lbu $t5, SDR_DATA_IO + PORT_OFFSET + NOP_OP_OFFSET($t0) /* 6 */
|
||||
|
||||
/* Command Inhibit */
|
||||
sll $v1, $v1, 8
|
||||
or $v1, $v1, $t5
|
||||
clock1
|
||||
lbu $t5, SDR_DATA_IO + PORT_OFFSET + NOP_OP_OFFSET($t0) /* 7 */
|
||||
|
||||
/* Command Inhibit */
|
||||
sll $v1, $v1, 8
|
||||
or $v1, $v1, $t5
|
||||
clock1
|
||||
lbu $t5, SDR_DATA_IO + PORT_OFFSET + NOP_OP_OFFSET($t0) /* 8 */
|
||||
|
||||
/* Command Inhibit */
|
||||
sll $v1, $v1, 8
|
||||
or $v1, $v1, $t5
|
||||
|
||||
.set macro
|
||||
|
||||
jr $ra
|
||||
nop
|
||||
|
||||
|
||||
/*
|
||||
* Sends PRECHARGE ALL command
|
||||
* C Prototype:
|
||||
* extern __attribute__((far)) void sdram_precharge_all(void);
|
||||
*/
|
||||
sdram_precharge_all:
|
||||
|
||||
la $t0, TRISA /* Port Base */
|
||||
li $t3, 0x8000 /* A10 */
|
||||
li $t4, (1<<CONTROL_CS_BIT) | (1<<CONTROL_RAS_BIT) | (1<<CONTROL_WE_BIT)
|
||||
|
||||
sw $t3, SDR_ADDRESS_IO + LAT_OFFSET + SET_OP_OFFSET($t0) /* A10 = 1 for Precharge ALL */
|
||||
|
||||
sync_clock
|
||||
|
||||
.set nomacro
|
||||
|
||||
/* Precharge All */
|
||||
sw $t4, SDR_CONTROL_IO + LAT_OFFSET + INV_OP_OFFSET($t0) /* LLHL */
|
||||
clock3
|
||||
|
||||
/* Command Inhibit */
|
||||
sw $t4, SDR_CONTROL_IO + LAT_OFFSET + INV_OP_OFFSET($t0) /* LLLL */
|
||||
clock3
|
||||
|
||||
.set macro
|
||||
|
||||
jr $ra
|
||||
nop
|
||||
|
||||
|
||||
/*
|
||||
* Sends PRECHARGE command
|
||||
* C Prototype:
|
||||
* extern __attribute__((far)) void sdram_precharge(void);
|
||||
*/
|
||||
sdram_precharge:
|
||||
|
||||
la $t0, TRISA /* Port Base */
|
||||
li $t3, 0x8000 /* A10 */
|
||||
li $t4, (1<<CONTROL_CS_BIT) | (1<<CONTROL_RAS_BIT) | (1<<CONTROL_WE_BIT)
|
||||
|
||||
sw $t3, SDR_ADDRESS_IO + LAT_OFFSET + CLR_OP_OFFSET($t0) /* A10 = 0 for Precharge */
|
||||
|
||||
sync_clock
|
||||
|
||||
.set nomacro
|
||||
|
||||
/* Precharge All */
|
||||
sw $t4, SDR_CONTROL_IO + LAT_OFFSET + INV_OP_OFFSET($t0) /* LLHL */
|
||||
clock3
|
||||
|
||||
/* Command Inhibit */
|
||||
sw $t4, SDR_CONTROL_IO + LAT_OFFSET + INV_OP_OFFSET($t0) /* LLLL */
|
||||
clock3
|
||||
|
||||
.set macro
|
||||
|
||||
jr $ra
|
||||
nop
|
||||
|
||||
|
||||
/*
|
||||
* Sends AUTO REFRESH command
|
||||
* All banks must be in PRECHARGEd state
|
||||
* C Prototype:
|
||||
* extern __attribute__((far)) void sdram_auto_refresh(void);
|
||||
*/
|
||||
sdram_auto_refresh:
|
||||
|
||||
la $t0, TRISA /* Port Base */
|
||||
li $t4, (1<<CONTROL_CS_BIT)|(1<<CONTROL_RAS_BIT)|(1<<CONTROL_CAS_BIT)
|
||||
|
||||
sync_clock
|
||||
|
||||
.set nomacro
|
||||
|
||||
/* Auto Refresh */
|
||||
sw $t4, SDR_CONTROL_IO + LAT_OFFSET + INV_OP_OFFSET($t0) /* LLLH */
|
||||
clock3
|
||||
|
||||
/* Command Inhibit */
|
||||
sw $t4, SDR_CONTROL_IO + LAT_OFFSET + INV_OP_OFFSET($t0) /* LLLL */
|
||||
clock3
|
||||
|
||||
.set macro
|
||||
|
||||
jr $ra
|
||||
nop
|
||||
|
||||
/*
|
||||
* Puts the SDRAM into the self refresh mode.
|
||||
* SDRAM retains data in this state.
|
||||
* C Prototype:
|
||||
* extern __attribute__((far)) void sdram_sleep(void);
|
||||
*/
|
||||
sdram_sleep:
|
||||
|
||||
la $t0, TRISA /* Port Base */
|
||||
li $t1, (1<<CKE_BIT)
|
||||
li $t4, (1<<CONTROL_CS_BIT)|(1<<CONTROL_RAS_BIT)|(1<<CONTROL_CAS_BIT)
|
||||
|
||||
sync_clock
|
||||
|
||||
.set nomacro
|
||||
|
||||
/* Auto Refresh */
|
||||
sw $t4, SDR_CONTROL_IO + LAT_OFFSET + INV_OP_OFFSET($t0) /* LLLH */
|
||||
sw $t1, SDR_CKE_IO + LAT_OFFSET + CLR_OP_OFFSET($t0) /* CKE low */
|
||||
clock2
|
||||
|
||||
/* Command Inhibit */
|
||||
sw $t4, SDR_CONTROL_IO + LAT_OFFSET + INV_OP_OFFSET($t0) /* LLLL */
|
||||
clock3
|
||||
|
||||
.set macro
|
||||
|
||||
jr $ra
|
||||
nop
|
||||
|
||||
/*
|
||||
* Takes the SDRAM out of the self refresh mode.
|
||||
* Parameters: none
|
||||
* C Prototype:
|
||||
* extern __attribute__((far)) void sdram_wake(void);
|
||||
*/
|
||||
sdram_wake:
|
||||
|
||||
la $t0, TRISA /* Port Base */
|
||||
li $t1, (1<<CKE_BIT)
|
||||
|
||||
sync_clock
|
||||
|
||||
.set nomacro
|
||||
|
||||
/* Command Inhibit */
|
||||
sw $t1, SDR_CKE_IO + LAT_OFFSET + SET_OP_OFFSET($t0) /* CKE low */
|
||||
clock3
|
||||
|
||||
/* Command Inhibit */
|
||||
clock4
|
||||
|
||||
/* Command Inhibit */
|
||||
clock4
|
||||
|
||||
.set macro
|
||||
|
||||
jr $ra
|
||||
nop
|
||||
|
||||
|
||||
/*
|
||||
* Selects the bank to which commands are issued
|
||||
* Parameters:
|
||||
* C Prototype:
|
||||
* extern __attribute__((far)) void sdram_bank(uint8_t bank);
|
||||
* (only lower 2 bits are of bank are currently significant)
|
||||
*/
|
||||
sdram_bank:
|
||||
|
||||
la $t0, TRISA /* Port Base */
|
||||
lw $t1, SDR_BANK_IO + LAT_OFFSET + NOP_OP_OFFSET($t0);
|
||||
and $t1, ~BANK_ALL_MASK
|
||||
sll $t2, $a0, BANK_0_BIT
|
||||
or $t1, $t2
|
||||
sw $t1, SDR_BANK_IO + LAT_OFFSET + NOP_OP_OFFSET($t0);
|
||||
|
||||
jr $ra
|
||||
nop
|
||||
21
share/lccom/Makefile
Normal file
21
share/lccom/Makefile
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
.SUFFIXES: .c .s .S .o
|
||||
|
||||
.c.o:
|
||||
lccom -target=mips-el $< > $*.s
|
||||
as -o $@ $*.s
|
||||
|
||||
.s.o:
|
||||
as -o $@ $<
|
||||
|
||||
.S.o:
|
||||
as -o $@ $<
|
||||
|
||||
all: hello
|
||||
|
||||
hello: hello.o sys.o
|
||||
ld -o hello -e main hello.o sys.o
|
||||
|
||||
clean:
|
||||
rm -f *.o *.s hello
|
||||
|
||||
6
share/lccom/hello.c
Normal file
6
share/lccom/hello.c
Normal file
@@ -0,0 +1,6 @@
|
||||
int main()
|
||||
{
|
||||
|
||||
write(1,"hello\n",6);
|
||||
exit(1);
|
||||
}
|
||||
100
share/lccom/sys.s
Normal file
100
share/lccom/sys.s
Normal file
@@ -0,0 +1,100 @@
|
||||
.text
|
||||
|
||||
#
|
||||
# int open( char* file, int flags, int mode )
|
||||
#
|
||||
open:
|
||||
|
||||
# errno handling code after syscall is not ideal,
|
||||
# but I don't think the assembler handles specifying
|
||||
# relocations for %hi and %lo yet, so the handling
|
||||
# shown in the retrobsd code is not really doable
|
||||
|
||||
syscall 5
|
||||
nop
|
||||
j serrn
|
||||
nop
|
||||
|
||||
jr $ra
|
||||
nop
|
||||
|
||||
#
|
||||
# int read( int fd, void* dest, int count)
|
||||
# returns: count of chars read or -1 if error (see errno)
|
||||
#
|
||||
read:
|
||||
|
||||
# errno handling code after syscall is not ideal,
|
||||
# but I don't think the assembler handles specifying
|
||||
# relocations for %hi and %lo yet, so the handling
|
||||
# shown in the retrobsd code is not really doable
|
||||
|
||||
syscall 3
|
||||
nop
|
||||
j serrn
|
||||
nop
|
||||
|
||||
jr $ra
|
||||
nop
|
||||
|
||||
|
||||
|
||||
#
|
||||
# int write( int fd, void* string, int count );
|
||||
# returns: count of chars written or -1 if error (see errno)
|
||||
#
|
||||
write:
|
||||
# errno handling code after syscall is not ideal,
|
||||
# but I don't think the assembler handles specifying
|
||||
# relocations for %hi and %lo yet, so the handling
|
||||
# shown in the retrobsd code is not really doable
|
||||
|
||||
syscall 4
|
||||
nop
|
||||
j serrn
|
||||
nop
|
||||
|
||||
jr $ra
|
||||
nop
|
||||
|
||||
#
|
||||
# int close( int fd );
|
||||
#
|
||||
close:
|
||||
# errno handling code after syscall is not ideal,
|
||||
# but I don't think the assembler handles specifying
|
||||
# relocations for %hi and %lo yet, so the handling
|
||||
# shown in the retrobsd code is not really doable
|
||||
|
||||
syscall 6
|
||||
nop
|
||||
j serrn
|
||||
nop
|
||||
|
||||
jr $ra
|
||||
nop
|
||||
|
||||
#
|
||||
# exit( int n );
|
||||
#
|
||||
exit:
|
||||
syscall 1
|
||||
nop
|
||||
|
||||
|
||||
serrn:
|
||||
la $t1, _errno
|
||||
sw $t0, 0($t1)
|
||||
jr $ra
|
||||
nop
|
||||
|
||||
.data
|
||||
_errno: .byte 0,0,0,0
|
||||
|
||||
.globl open
|
||||
.globl read
|
||||
.globl write
|
||||
.globl close
|
||||
.globl exit
|
||||
.globl _errno
|
||||
|
||||
27
share/smallc/Makefile
Normal file
27
share/smallc/Makefile
Normal file
@@ -0,0 +1,27 @@
|
||||
|
||||
.SUFFIXES: .c .s .o
|
||||
|
||||
.c.o:
|
||||
smallc $<
|
||||
as -o $@ $*.s
|
||||
|
||||
.s.o:
|
||||
as -o $@ $<
|
||||
|
||||
all: primelist primesum test1 test2
|
||||
|
||||
primelist: primelist.o lib.o sys.o
|
||||
ld -o primelist -e _main primelist.o lib.o sys.o
|
||||
|
||||
primesum: primesum.o lib.o sys.o
|
||||
ld -o primesum -e _main primesum.o lib.o sys.o
|
||||
|
||||
test1: test1.o lib.o sys.o
|
||||
ld -o $@ -e _main test1.o lib.o sys.o
|
||||
|
||||
test2: test2.o lib.o sys.o
|
||||
ld -o $@ -e _main test2.o lib.o sys.o
|
||||
|
||||
clean:
|
||||
rm *.o primelist primesum test1 test2 primelist.s primesum.s test1.s test2.s
|
||||
|
||||
37
share/smallc/lib.c
Normal file
37
share/smallc/lib.c
Normal file
@@ -0,0 +1,37 @@
|
||||
w_s(s)
|
||||
char*s;
|
||||
{
|
||||
write( 1, s, strlen(s) );
|
||||
}
|
||||
|
||||
strlen(s)
|
||||
char*s;
|
||||
{
|
||||
char *e;
|
||||
e = s;
|
||||
while(*e != 0) ++e;
|
||||
return e-s;
|
||||
}
|
||||
|
||||
w_c(c)
|
||||
char c;
|
||||
{
|
||||
write( 1, &c, 1 );
|
||||
}
|
||||
|
||||
w_n(number, radix)
|
||||
int number, radix;
|
||||
{
|
||||
int i;
|
||||
char *drp;
|
||||
drp = "0123456789ABCDEF";
|
||||
|
||||
if (number < 0 & radix == 10)
|
||||
{
|
||||
w_c('-');
|
||||
number = -number;
|
||||
}
|
||||
if ((i = number / radix) != 0)
|
||||
w_n(i, radix);
|
||||
w_c(drp[number % radix]);
|
||||
}
|
||||
24
share/smallc/primelist.c
Normal file
24
share/smallc/primelist.c
Normal file
@@ -0,0 +1,24 @@
|
||||
main()
|
||||
{
|
||||
int n;
|
||||
for(n=2;n<100;++n){
|
||||
if( isprime(n) ){
|
||||
w_n(n,10);
|
||||
w_c(' ');
|
||||
}
|
||||
}
|
||||
w_c('\n');
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
isprime(n)
|
||||
int n;
|
||||
{
|
||||
int j;
|
||||
if( n == 2 ) return 1;
|
||||
if( n % 2 == 0 ) return 0;
|
||||
for(j=3;j*j<=n;j+=2)
|
||||
if( n % j == 0 ) return 0;
|
||||
return 1;
|
||||
}
|
||||
27
share/smallc/primesum.c
Normal file
27
share/smallc/primesum.c
Normal file
@@ -0,0 +1,27 @@
|
||||
main()
|
||||
{
|
||||
int sum;
|
||||
int n;
|
||||
sum = 0;
|
||||
for(n=2;n<10000;++n){
|
||||
if( isprime(n) ){
|
||||
sum += n;
|
||||
}
|
||||
}
|
||||
w_s("sum of primes less than 10000: ");
|
||||
w_n(sum,10);
|
||||
w_c('\n');
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
isprime(n)
|
||||
int n;
|
||||
{
|
||||
int j;
|
||||
if( n == 2 ) return 1;
|
||||
if( n % 2 == 0 ) return 0;
|
||||
for(j=3;j*j<=n;j+=2)
|
||||
if( n % j == 0 ) return 0;
|
||||
return 1;
|
||||
}
|
||||
159
share/smallc/sys.s
Normal file
159
share/smallc/sys.s
Normal file
@@ -0,0 +1,159 @@
|
||||
.text
|
||||
|
||||
#
|
||||
# int open( char* file, int flags, int mode )
|
||||
#
|
||||
_open:
|
||||
lw $a0, 8($sp)
|
||||
lw $a1, 4($sp)
|
||||
lw $a2, 0($sp)
|
||||
|
||||
# errno handling code after syscall is not ideal,
|
||||
# but I don't think the assembler handles specifying
|
||||
# relocations for %hi and %lo yet, so the handling
|
||||
# shown in the retrobsd code is not really doable
|
||||
|
||||
syscall 5
|
||||
nop
|
||||
j serrn
|
||||
nop
|
||||
|
||||
jr $ra
|
||||
nop
|
||||
|
||||
#
|
||||
# int read( int fd, void* dest, int count)
|
||||
# returns: count of chars read or -1 if error (see errno)
|
||||
#
|
||||
_read:
|
||||
lw $a0, 8($sp)
|
||||
lw $a1, 4($sp)
|
||||
lw $a2, 0($sp)
|
||||
|
||||
# errno handling code after syscall is not ideal,
|
||||
# but I don't think the assembler handles specifying
|
||||
# relocations for %hi and %lo yet, so the handling
|
||||
# shown in the retrobsd code is not really doable
|
||||
|
||||
syscall 3
|
||||
nop
|
||||
j serrn
|
||||
nop
|
||||
|
||||
jr $ra
|
||||
nop
|
||||
|
||||
|
||||
|
||||
#
|
||||
# int write( int fd, void* string, int count );
|
||||
# returns: count of chars written or -1 if error (see errno)
|
||||
#
|
||||
_write:
|
||||
lw $a0, 8($sp)
|
||||
lw $a1, 4($sp)
|
||||
lw $a2, 0($sp)
|
||||
|
||||
# errno handling code after syscall is not ideal,
|
||||
# but I don't think the assembler handles specifying
|
||||
# relocations for %hi and %lo yet, so the handling
|
||||
# shown in the retrobsd code is not really doable
|
||||
|
||||
syscall 4
|
||||
nop
|
||||
j serrn
|
||||
nop
|
||||
|
||||
jr $ra
|
||||
nop
|
||||
|
||||
#
|
||||
# int close( int fd );
|
||||
#
|
||||
_close:
|
||||
lw $a0, 0($sp)
|
||||
|
||||
# errno handling code after syscall is not ideal,
|
||||
# but I don't think the assembler handles specifying
|
||||
# relocations for %hi and %lo yet, so the handling
|
||||
# shown in the retrobsd code is not really doable
|
||||
|
||||
syscall 6
|
||||
nop
|
||||
j serrn
|
||||
nop
|
||||
|
||||
jr $ra
|
||||
nop
|
||||
|
||||
#
|
||||
# exit( int n );
|
||||
#
|
||||
_exit:
|
||||
lw $a0, 0($sp)
|
||||
addiu $sp, $sp, -4
|
||||
syscall 1
|
||||
nop
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
serrn:
|
||||
la $t1, _errno
|
||||
sw $t0, 0($t1)
|
||||
jr $ra
|
||||
nop
|
||||
|
||||
#
|
||||
# $v0 = value to switch on
|
||||
# 0($sp) = pointer to list of value,ptr cases
|
||||
# ended where ptr=0, value is used as pointer to jump to in default case
|
||||
# looks like stack is popped as part of this
|
||||
# FIXME - The assembler/linker only stores the bottom 16 bits
|
||||
# of the labels in pair, so we construct the address by merging the 16 bits
|
||||
# in the cell with the upper 16 bits in the return address of the code that
|
||||
# called this. Is there a way to get the assembler linker to store the full
|
||||
# address? If so, that should be used instead.
|
||||
#
|
||||
Tcase:
|
||||
lw $t1, 0($sp) # t1=pointer to list of value/ptr pairs
|
||||
addiu $sp, $sp, 4 # pop stack that held pointer
|
||||
.Tcl:
|
||||
lw $t2, 0($t1) # get value from pair
|
||||
lw $t3, 4($t1) # get ptr from pair
|
||||
beq $t3, $zero, .Tcd
|
||||
nop
|
||||
|
||||
beq $t2, $v0, .Tcm
|
||||
nop
|
||||
|
||||
addiu $t1, $t1, 8 # t1 += size of pair
|
||||
j .Tcl
|
||||
nop
|
||||
|
||||
.Tcd:
|
||||
move $t3, $t2
|
||||
.Tcm:
|
||||
lui $t2, 0xffff
|
||||
and $t2, $t2, $ra
|
||||
or $t3, $t3, $t2
|
||||
jr $t3
|
||||
nop
|
||||
|
||||
Tcallstk:
|
||||
jr $t1
|
||||
nop
|
||||
|
||||
.data
|
||||
_errno: .byte 0,0,0,0
|
||||
|
||||
.globl _open
|
||||
.globl _read
|
||||
.globl _write
|
||||
.globl _close
|
||||
.globl _exit
|
||||
.globl _errno
|
||||
.globl Tcase
|
||||
.globl Tcallstk
|
||||
|
||||
231
share/smallc/test1.c
Normal file
231
share/smallc/test1.c
Normal file
@@ -0,0 +1,231 @@
|
||||
main()
|
||||
{
|
||||
int a,b,c,d;
|
||||
int arr[5];
|
||||
int *pi;
|
||||
char arrc[5];
|
||||
char *pic;
|
||||
int s1,s2;
|
||||
int z;
|
||||
int t;
|
||||
int *pip;
|
||||
int *picp;
|
||||
int e1,e2;
|
||||
|
||||
a = 21;
|
||||
b = 31;
|
||||
c = 71;
|
||||
d = 82;
|
||||
|
||||
arr[0] = 10;
|
||||
arr[1] = 20;
|
||||
arr[2] = 30;
|
||||
arr[3] = 40;
|
||||
arr[4] = 50;
|
||||
pi = &arr[0];
|
||||
|
||||
arrc[0] = 13;
|
||||
arrc[1] = 23;
|
||||
arrc[2] = 33;
|
||||
arrc[3] = 43;
|
||||
arrc[4] = 53;
|
||||
pic = &arrc[0];
|
||||
|
||||
w_s("21+31="); w_n(a+b,10); w_s(" (52)\n");
|
||||
w_s("21-31="); w_n(a-b,10); w_s(" (-10)\n");
|
||||
w_s("21&71="); w_n(a&c,10); w_s(" (5)\n");
|
||||
w_s("21|82="); w_n(a|d,10); w_s(" (87)\n");
|
||||
w_s("21^82="); w_n(a^d,10); w_s(" (71)\n");
|
||||
w_s("21*82="); w_n(a*d,10); w_s(" (1722)\n");
|
||||
w_s("82%21="); w_n(d%a,10); w_s(" (19)\n");
|
||||
w_s("82/21="); w_n(d/a,10); w_s(" (3)\n");
|
||||
w_s("*pi="); w_n(*pi,10); w_s(" (10)\n");
|
||||
w_s("*pi+1="); w_n(*pi+1,10); w_s(" (11)\n");
|
||||
w_s("*(pi+1)="); w_n(*(pi+1),10); w_s(" (20)\n");
|
||||
w_s("&arr[3]-&arr[0]="); w_n(&arr[3]-&arr[0],10); w_s(" (3)\n");
|
||||
|
||||
w_s("*pic="); w_n(*pic,10); w_s(" (13)\n");
|
||||
w_s("*pic+1="); w_n(*pic+1,10); w_s(" (14)\n");
|
||||
w_s("*(pic+1)="); w_n(*(pic+1),10); w_s(" (23)\n");
|
||||
w_s("&arrc[3]-&arrc[0]="); w_n(&arrc[3]-&arrc[0],10); w_s(" (3)\n");
|
||||
|
||||
s1 = 3;
|
||||
s2 = -200;
|
||||
w_s("82<<3="); w_n(d<<s1,10); w_s(" (656)\n");
|
||||
w_s("82>>3="); w_n(d>>s1,10); w_s(" (10)\n");
|
||||
w_s("-200>>3="); w_n(s2>>s1,10); w_s(" (-25)\n");
|
||||
w_s("-200<<3="); w_n(s2<<s1,10); w_s(" (-1600)\n");
|
||||
|
||||
w_s("-s1="); w_n(-s1,10); w_s(" (-3)\n");
|
||||
w_s("-s2="); w_n(-s2,10); w_s(" (200)\n");
|
||||
|
||||
w_s("~82="); w_n(~d,10); w_s(" (-83)\n");
|
||||
|
||||
z = 0;
|
||||
|
||||
w_s("!82="); w_n(!d,10); w_s(" (0)\n");
|
||||
w_s("!0="); w_n(!z,10); w_s(" (1)\n");
|
||||
|
||||
t = 0;
|
||||
if(t) printt(t,"failure\n"); else printt(t,"success\n");
|
||||
t = 1;
|
||||
if(t) printt(t,"success\n"); else printt(t,"failure\n");
|
||||
t = 8;
|
||||
if(t) printt(t,"success\n"); else printt(t,"failure\n");
|
||||
t = -2;
|
||||
if(t) printt(t,"success\n"); else printt(t,"failure\n");
|
||||
|
||||
w_s("0&&0="); w_n(z&&z,10); w_s(" (0)\n");
|
||||
w_s("0&&21="); w_n(z&&a,10); w_s(" (0)\n");
|
||||
w_s("3&&21="); w_n(s1&&a,10); w_s(" (1)\n");
|
||||
w_s("21&&3="); w_n(a&&s1,10); w_s(" (1)\n");
|
||||
|
||||
w_s("0||0="); w_n(z||z,10); w_s(" (0)\n");
|
||||
w_s("0||21="); w_n(z||a,10); w_s(" (1)\n");
|
||||
w_s("3||21="); w_n(s1||a,10); w_s(" (1)\n");
|
||||
w_s("21||3="); w_n(a||s1,10); w_s(" (1)\n");
|
||||
|
||||
pi = 4;
|
||||
w_s("pi++="); w_n(pi++,10); w_s(" (4)\n");
|
||||
w_s("pi="); w_n(pi,10); w_s(" (8)\n");
|
||||
w_s("++pi="); w_n(++pi,10); w_s(" (12)\n");
|
||||
w_s("pi--="); w_n(pi--,10); w_s(" (12)\n");
|
||||
w_s("pi="); w_n(pi,10); w_s(" (8)\n");
|
||||
w_s("--pi="); w_n(--pi,10); w_s(" (4)\n");
|
||||
|
||||
pic = 4;
|
||||
w_s("pic++="); w_n(pic++,10); w_s(" (4)\n");
|
||||
w_s("pic="); w_n(pic,10); w_s(" (5)\n");
|
||||
w_s("++pic="); w_n(++pic,10); w_s(" (6)\n");
|
||||
w_s("pic--="); w_n(pic--,10); w_s(" (6)\n");
|
||||
w_s("pic="); w_n(pic,10); w_s(" (5)\n");
|
||||
w_s("--pic="); w_n(--pic,10); w_s(" (4)\n");
|
||||
|
||||
t = 4;
|
||||
w_s("t++="); w_n(t++,10); w_s(" (4)\n");
|
||||
w_s("t="); w_n(t,10); w_s(" (5)\n");
|
||||
w_s("++t="); w_n(++t,10); w_s(" (6)\n");
|
||||
w_s("t--="); w_n(t--,10); w_s(" (6)\n");
|
||||
w_s("t="); w_n(t,10); w_s(" (5)\n");
|
||||
w_s("--t="); w_n(--t,10); w_s(" (4)\n");
|
||||
|
||||
t = 4;
|
||||
w_s("t==4="); w_n(t==4,10); w_s(" (1)\n");
|
||||
w_s("t==3="); w_n(t==3,10); w_s(" (0)\n");
|
||||
w_s("t==5="); w_n(t==5,10); w_s(" (0)\n");
|
||||
t = -4;
|
||||
w_s("t==-4="); w_n(t==-4,10); w_s(" (1)\n");
|
||||
w_s("t==-3="); w_n(t==-3,10); w_s(" (0)\n");
|
||||
w_s("t==-5="); w_n(t==-5,10); w_s(" (0)\n");
|
||||
w_s("t==4="); w_n(t==4,10); w_s(" (0)\n");
|
||||
w_s("t==3="); w_n(t==3,10); w_s(" (0)\n");
|
||||
w_s("t==5="); w_n(t==5,10); w_s(" (0)\n");
|
||||
|
||||
t = 4;
|
||||
w_s("t!=4="); w_n(t!=4,10); w_s(" (0)\n");
|
||||
w_s("t!=3="); w_n(t!=3,10); w_s(" (1)\n");
|
||||
w_s("t!=5="); w_n(t!=5,10); w_s(" (1)\n");
|
||||
t = -4;
|
||||
w_s("t!=-4="); w_n(t!=-4,10); w_s(" (0)\n");
|
||||
w_s("t!=-3="); w_n(t!=-3,10); w_s(" (1)\n");
|
||||
w_s("t!=-5="); w_n(t!=-5,10); w_s(" (1)\n");
|
||||
w_s("t!=4="); w_n(t!=4,10); w_s(" (1)\n");
|
||||
w_s("t!=3="); w_n(t!=3,10); w_s(" (1)\n");
|
||||
w_s("t!=5="); w_n(t!=5,10); w_s(" (1)\n");
|
||||
|
||||
t = 4;
|
||||
w_s("t<4="); w_n(t<4,10); w_s(" (0)\n");
|
||||
w_s("t<3="); w_n(t<3,10); w_s(" (0)\n");
|
||||
w_s("t<5="); w_n(t<5,10); w_s(" (1)\n");
|
||||
w_s("t<-1="); w_n(t<-1,10); w_s(" (0)\n");
|
||||
|
||||
w_s("t<=4="); w_n(t<=4,10); w_s(" (1)\n");
|
||||
w_s("t<=3="); w_n(t<=3,10); w_s(" (0)\n");
|
||||
w_s("t<=5="); w_n(t<=5,10); w_s(" (1)\n");
|
||||
w_s("t<=-1="); w_n(t<=-1,10); w_s(" (0)\n");
|
||||
|
||||
t = 4;
|
||||
w_s("t>4="); w_n(t>4,10); w_s(" (0)\n");
|
||||
w_s("t>3="); w_n(t>3,10); w_s(" (1)\n");
|
||||
w_s("t>5="); w_n(t>5,10); w_s(" (0)\n");
|
||||
w_s("t>-1="); w_n(t>-1,10); w_s(" (1)\n");
|
||||
|
||||
w_s("t>=4="); w_n(t>=4,10); w_s(" (1)\n");
|
||||
w_s("t>=3="); w_n(t>=3,10); w_s(" (1)\n");
|
||||
w_s("t>=5="); w_n(t>=5,10); w_s(" (0)\n");
|
||||
w_s("t>=-1="); w_n(t>=-1,10); w_s(" (1)\n");
|
||||
|
||||
|
||||
pi = -100;
|
||||
w_s("pi<4="); w_n(pi<4,10); w_s(" (0)\n");
|
||||
w_s("pi<3="); w_n(pi<3,10); w_s(" (0)\n");
|
||||
w_s("pi<-100="); w_n(pi<-100,10); w_s(" (0)\n");
|
||||
w_s("pi<-1="); w_n(pi<-1,10); w_s(" (1)\n");
|
||||
|
||||
w_s("pi<=4="); w_n(pi<=4,10); w_s(" (0)\n");
|
||||
w_s("pi<=3="); w_n(pi<=3,10); w_s(" (0)\n");
|
||||
w_s("pi<=-100="); w_n(pi<=-100,10); w_s(" (1)\n");
|
||||
w_s("pi<=-1="); w_n(pi<=-1,10); w_s(" (1)\n");
|
||||
|
||||
pi = -100;
|
||||
w_s("pi>4="); w_n(pi>4,10); w_s(" (1)\n");
|
||||
w_s("pi>3="); w_n(pi>3,10); w_s(" (1)\n");
|
||||
w_s("pi>-100="); w_n(pi>-100,10); w_s(" (0)\n");
|
||||
w_s("pi>-1="); w_n(pi>-1,10); w_s(" (0)\n");
|
||||
|
||||
w_s("pi>=4="); w_n(pi>=4,10); w_s(" (1)\n");
|
||||
w_s("pi>=3="); w_n(pi>=3,10); w_s(" (1)\n");
|
||||
w_s("pi>=-100="); w_n(pi>=-100,10); w_s(" (1)\n");
|
||||
w_s("pi>=-1="); w_n(pi>=-1,10); w_s(" (0)\n");
|
||||
|
||||
w_s("switch test: ");
|
||||
switch(t)
|
||||
{
|
||||
case 3:
|
||||
w_s("failure");
|
||||
break;
|
||||
case 4:
|
||||
w_s("success");
|
||||
break;
|
||||
case 5:
|
||||
w_s("failure");
|
||||
break;
|
||||
}
|
||||
w_s("\n");
|
||||
|
||||
w_s("switch fallthrough test: ");
|
||||
switch(t)
|
||||
{
|
||||
case 3:
|
||||
w_s("failure");
|
||||
break;
|
||||
case 4:
|
||||
w_s("OKSOFAR: ");
|
||||
case 5:
|
||||
w_s("success if oksofar printed before this in caps");
|
||||
break;
|
||||
}
|
||||
w_s("\n");
|
||||
|
||||
pi = &arr[0];
|
||||
pip = &arr[3];
|
||||
w_s("*pip-*pi: "); w_n(*pip-*pi,10); w_s(" 30\n");
|
||||
w_s("pip-pi: "); w_n(pip-pi,10); w_s(" 3\n");
|
||||
w_s("*pip: "); w_n( *pip, 10 ); w_s(" 40\n");
|
||||
w_s("*(pip-3): "); w_n(*(pip-3),10); w_s(" 10\n");
|
||||
w_s("*&arr[3]: "); w_n( *&arr[3], 10 ); w_s(" 40\n");
|
||||
// The following causes an address error, so it has been removed
|
||||
// and needs to be fixed
|
||||
w_s("*(&arr[3]-3): "); w_n(0/**(&arr[3]-3)*/,10); w_s(" 10 broken - causes address error\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
printt(t,str)
|
||||
int t;
|
||||
char *str;
|
||||
{
|
||||
w_s("bool t test on value ");
|
||||
w_n(t,10);
|
||||
w_s(" ");
|
||||
w_s(str);
|
||||
}
|
||||
150
share/smallc/test2.c
Normal file
150
share/smallc/test2.c
Normal file
@@ -0,0 +1,150 @@
|
||||
|
||||
#define O_RDONLY 0x0000 /* open for reading only */
|
||||
#define O_WRONLY 0x0001 /* open for writing only */
|
||||
#define O_RDWR 0x0002 /* open for reading and writing */
|
||||
#define O_ACCMODE 0x0003 /* mask for above modes */
|
||||
|
||||
#define O_NONBLOCK 0x0004 /* no delay */
|
||||
#define O_APPEND 0x0008 /* set append mode */
|
||||
#define O_SHLOCK 0x0010 /* open with shared file lock */
|
||||
#define O_EXLOCK 0x0020 /* open with exclusive file lock */
|
||||
#define O_ASYNC 0x0040 /* signal pgrp when data ready */
|
||||
#define O_FSYNC 0x0080 /* synchronous writes */
|
||||
#define O_CREAT 0x0200 /* create if nonexistant */
|
||||
#define O_TRUNC 0x0400 /* truncate to zero length */
|
||||
#define O_EXCL 0x0800 /* error if already exists */
|
||||
|
||||
int aaa;
|
||||
int bbb;
|
||||
int ccc;
|
||||
char gc;
|
||||
char gbuffer[3];
|
||||
int gibuffer[4];
|
||||
|
||||
extern errno;
|
||||
|
||||
main()
|
||||
{
|
||||
char b;
|
||||
int la;
|
||||
unsigned int u1, u2;
|
||||
int s1, s2;
|
||||
unsigned char uc1, uc2;
|
||||
char sc1, sc2;
|
||||
int fd;
|
||||
char buffer[6];
|
||||
int ibuffer[7];
|
||||
|
||||
w_s( "sizeof(uc1): "); w_n( sizeof(uc1), 10 ); w_s( " 1\n" );
|
||||
w_s( "sizeof(sc1): "); w_n( sizeof(sc1), 10 ); w_s( " 1\n" );
|
||||
w_s( "sizeof(u1): "); w_n( sizeof(u1), 10 ); w_s( " 4\n" );
|
||||
w_s( "sizeof(s1): "); w_n( sizeof(s1), 10 ); w_s( " 4\n" );
|
||||
w_s( "sizeof(aaa): "); w_n( sizeof(aaa), 10 ); w_s( " 4\n" );
|
||||
w_s( "sizeof(bbb): "); w_n( sizeof(bbb), 10 ); w_s( " 4\n" );
|
||||
w_s( "sizeof(gc): "); w_n( sizeof(gc), 10 ); w_s( " 1\n" );
|
||||
w_s( "sizeof(buffer): "); w_n( sizeof(buffer), 10 ); w_s( " 6\n" );
|
||||
w_s( "sizeof(ibuffer): "); w_n( sizeof(ibuffer), 10 ); w_s( " 28\n" );
|
||||
w_s( "sizeof(char): "); w_n( sizeof(char), 10 ); w_s( " 1\n" );
|
||||
w_s( "sizeof(int): "); w_n( sizeof(int), 10 ); w_s( " 4\n" );
|
||||
w_s( "sizeof(gbuffer): "); w_n( sizeof(gbuffer), 10 ); w_s( " 3\n" );
|
||||
w_s( "sizeof(gibuffer): "); w_n( sizeof(gibuffer), 10 ); w_s( " 16\n" );
|
||||
// sizeof(ibuffer[0]) is not supported, so the following can be used...
|
||||
w_s( "sizeof(ibuffer)/sizeof(int): "); w_n( sizeof(ibuffer)/sizeof(int), 10 ); w_s( " 7\n" );
|
||||
|
||||
aaa = 1;
|
||||
bbb = 2;
|
||||
la = 4;
|
||||
|
||||
|
||||
w_n(aaa,10);
|
||||
w_s(" 1\n");
|
||||
w_n(bbb,10);
|
||||
w_s(" 2\n");
|
||||
w_n(la,10);
|
||||
w_s(" 4\n");
|
||||
|
||||
uc1 = 0x80;
|
||||
sc1 = 0x80;
|
||||
s1 = uc1;
|
||||
s2 = sc1;
|
||||
w_s( "unsigned char (0x80) -> int: "); w_n( s1, 10 ); w_s( " 128\n" );
|
||||
w_s( "signed char (0x80) -> int: "); w_n( s2, 10 ); w_s( " -128\n" );
|
||||
|
||||
u1 = uc1;
|
||||
u2 = sc1;
|
||||
w_s( "unsigned char (0x80) -> unsigned: "); w_n( u1, 10 ); w_s( " 128\n" );
|
||||
w_s( "signed char (0x80) -> unsigned: "); w_n( u2, 10 ); w_s( " -128\n" );
|
||||
|
||||
la = errno;
|
||||
w_s("errno: "); w_n(la,10); w_s( " 0\n");
|
||||
|
||||
write( 1, "abcd ", 5 );
|
||||
la = errno;
|
||||
w_s("errno after good write call: "); w_n(la,10); w_s( " 0\n");
|
||||
|
||||
write( 10, "abcde", 5 );
|
||||
la = errno;
|
||||
w_s("errno after bad write call: "); w_n(la,10); w_s( " 9\n");
|
||||
|
||||
write( 1, "abcd ", 5 );
|
||||
la = errno;
|
||||
w_s("good write after failed should not overwrite errno: "); w_n(la,10); w_s( " 9\n");
|
||||
|
||||
errno = 0;
|
||||
write( 1, "abcd ", 5 );
|
||||
la = errno;
|
||||
w_s("good write after errno set to zero: "); w_n(la,10); w_s( " 0\n");
|
||||
|
||||
la = write( 1, "abcd ", 5 );
|
||||
w_s( "write() return: " ); w_n(la,10); w_s( " 5\n" );
|
||||
|
||||
la = write( 10, "abcd ", 5 );
|
||||
w_s( "write(bad fd) return: " ); w_n(la,10); w_s( " -1\n" );
|
||||
|
||||
fd = open( "/a.txt", O_WRONLY|O_CREAT, 0666 );
|
||||
if( fd != -1 )
|
||||
{
|
||||
w_s("open success\n");
|
||||
la = write( fd, "abcd\n", 5 );
|
||||
if( la == 5 ) w_s( "write success\n" ); else w_s( "write failed\n" );
|
||||
la = close( fd );
|
||||
if( la != -1 ) w_s( "close success\n" ); else w_s( "close failed\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
w_s("open failed\n");
|
||||
}
|
||||
|
||||
buffer[0] = 0;
|
||||
buffer[1] = 0;
|
||||
buffer[2] = 0;
|
||||
buffer[3] = 0;
|
||||
buffer[4] = 0;
|
||||
buffer[5] = 0;
|
||||
|
||||
fd = open( "/a.txt", O_RDONLY, 0666 );
|
||||
if( fd != -1 )
|
||||
{
|
||||
w_s("open success\n");
|
||||
la = read( fd, buffer, 5 );
|
||||
w_s( buffer );
|
||||
if( la == 5 ) w_s( "read success\n" ); else w_s( "read failed\n" );
|
||||
la = close( fd );
|
||||
if( la != -1 ) w_s( "close success\n" ); else w_s( "close failed\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
w_s("open failed\n");
|
||||
}
|
||||
|
||||
if( buffer[0] != 'a') w_s("data0 readback from file MISMATCH\n");
|
||||
if( buffer[1] != 'b') w_s("data1 readback from file MISMATCH\n");
|
||||
if( buffer[2] != 'c') w_s("data2 readback from file MISMATCH\n");
|
||||
if( buffer[3] != 'd') w_s("data3 readback from file MISMATCH\n");
|
||||
if( buffer[4] != '\n') w_s("data4 readback from file MISMATCH\n");
|
||||
|
||||
if( buffer[0] != 'a' || buffer[1] != 'b' || buffer[2] != 'c' || buffer[3] != 'd' || buffer[4] != '\n') w_s("data readback from file MISMATCH\n");
|
||||
else w_s("data readback from file OK\n");
|
||||
|
||||
exit(0);
|
||||
}
|
||||
@@ -13,10 +13,10 @@ CFLAGS += -Werror
|
||||
SUBDIR = adb adc-demo ar as awk basic cc chflags chpass \
|
||||
cpp dc diff env find forth fstat glcdtest hostname \
|
||||
id la lcc lcpp ld ls login make man med \
|
||||
more nm passwd picoc portio printf pwm \
|
||||
more nm passwd portio printf pwm \
|
||||
ranlib re renice retroforth scm setty sl \
|
||||
sed sh smallc stty sysctl test uname wiznet xargs \
|
||||
zmodem gtest
|
||||
zmodem gtest cron uflash_r picoc-1 lccom-1
|
||||
|
||||
# /sbin
|
||||
SUBDIR += chown chroot disktool fsck getty init \
|
||||
@@ -36,7 +36,7 @@ STD = basename cal cat cb chgrp chmod cmp col comm cp date dd du \
|
||||
echo ed fgrep file grep head hostid join kill last ln \
|
||||
mesg mkdir mv nice od pagesize pr printenv pwd rev rm rmail rmdir \
|
||||
size sleep sort split sum sync tail tar tee time touch tr \
|
||||
tsort tty uniq w wc whereis who
|
||||
tsort tty uniq w wc whereis who uflash
|
||||
|
||||
# C programs that live in the current directory and need explicit make lines.
|
||||
#
|
||||
|
||||
@@ -58,6 +58,8 @@ enum {
|
||||
LTEXT, /* .text */
|
||||
LEQU, /* .equ */
|
||||
LWORD, /* .word */
|
||||
LBYTE, /* .byte */
|
||||
LSPACE, /* .space */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -589,6 +591,7 @@ int lookacmd ()
|
||||
break;
|
||||
case 'b':
|
||||
if (! strcmp (".bss", name)) return (LBSS);
|
||||
if (! strcmp (".byte", name)) return (LBYTE);
|
||||
break;
|
||||
case 'c':
|
||||
if (! strcmp (".comm", name)) return (LCOMM);
|
||||
@@ -604,6 +607,7 @@ int lookacmd ()
|
||||
break;
|
||||
case 's':
|
||||
if (! strcmp (".short", name)) return (LSHORT);
|
||||
if (! strcmp (".space", name)) return (LSPACE);
|
||||
if (! strcmp (".strng", name)) return (LSTRNG);
|
||||
break;
|
||||
case 't':
|
||||
@@ -1411,7 +1415,7 @@ void makeascii ()
|
||||
void pass1 ()
|
||||
{
|
||||
register int clex;
|
||||
int cval, tval, csegm;
|
||||
int cval, tval, csegm, nbytes, c, nwords;
|
||||
register unsigned addr;
|
||||
|
||||
segm = STEXT;
|
||||
@@ -1506,7 +1510,7 @@ void pass1 ()
|
||||
addr |= RSETINDEX (extref);
|
||||
fputword (intval, sfile[segm]);
|
||||
fputrel (addr, rfile[segm]);
|
||||
count[segm] += 2;
|
||||
count[segm] += WORDSZ;
|
||||
clex = getlex (&cval);
|
||||
if (clex != ',') {
|
||||
ungetlex (clex, cval);
|
||||
@@ -1514,6 +1518,41 @@ void pass1 ()
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LBYTE:
|
||||
nbytes = 0;
|
||||
for (;;) {
|
||||
getexpr (&cval);
|
||||
fputc (intval, sfile[segm]);
|
||||
nbytes++;
|
||||
clex = getlex (&cval);
|
||||
if (clex != ',') {
|
||||
ungetlex (clex, cval);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
c = (WORDSZ - (unsigned) nbytes % WORDSZ) % WORDSZ;
|
||||
count[segm] += nbytes + c;
|
||||
nwords = (unsigned) (nbytes + c) / WORDSZ;
|
||||
while (c--)
|
||||
fputc (0, sfile[segm]);
|
||||
while (nwords--)
|
||||
fputrel (RABS, rfile[segm]);
|
||||
break;
|
||||
case LSPACE:
|
||||
getexpr (&cval);
|
||||
for (nbytes=0;nbytes<intval;nbytes++) {
|
||||
fputc (0, sfile[segm]);
|
||||
}
|
||||
c = (WORDSZ - (unsigned) nbytes % WORDSZ) % WORDSZ;
|
||||
count[segm] += nbytes + c;
|
||||
nwords = (unsigned) (nbytes + c) / WORDSZ;
|
||||
while (c--)
|
||||
fputc (0, sfile[segm]);
|
||||
while (nwords--)
|
||||
fputrel (RABS, rfile[segm]);
|
||||
break;
|
||||
|
||||
case LASCII:
|
||||
makeascii ();
|
||||
break;
|
||||
|
||||
109
src/cmd/cron/Makefile
Executable file
109
src/cmd/cron/Makefile
Executable file
@@ -0,0 +1,109 @@
|
||||
#/* Copyright 1988,1990,1993,1994 by Paul Vixie
|
||||
# * All rights reserved
|
||||
# *
|
||||
# * Distribute freely, except: don't remove my name from the source or
|
||||
# * documentation (don't take credit for my work), mark your changes (don't
|
||||
# * get me blamed for your possible bugs), don't alter or remove this
|
||||
# * notice. May be sold if buildable source is provided to buyer. No
|
||||
# * warrantee of any kind, express or implied, is included with this
|
||||
# * software; use at your own risk, responsibility for damages (if any) to
|
||||
# * anyone resulting from the use of this software rests entirely with the
|
||||
# * user.
|
||||
# *
|
||||
# * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
|
||||
# * I'll try to keep a version up to date. I can be reached as follows:
|
||||
# * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
|
||||
# */
|
||||
|
||||
# Makefile for vixie's cron
|
||||
#
|
||||
# $Id: Makefile,v 2.9 1994/01/15 20:43:43 vixie Exp $
|
||||
#
|
||||
# vix 03mar88 [moved to RCS, rest of log is in there]
|
||||
# vix 30mar87 [goodbye, time.c; hello, getopt]
|
||||
# vix 12feb87 [cleanup for distribution]
|
||||
# vix 30dec86 [written]
|
||||
|
||||
# NOTES:
|
||||
# 'make' can be done by anyone
|
||||
# 'make install' must be done by root
|
||||
#
|
||||
# this package needs getopt(3), bitstring(3), and BSD install(8).
|
||||
#
|
||||
# the configurable stuff in this makefile consists of compilation
|
||||
# options (use -O, cron runs forever) and destination directories.
|
||||
# SHELL is for the 'augumented make' systems where 'make' imports
|
||||
# SHELL from the environment and then uses it to run its commands.
|
||||
# if your environment SHELL variable is /bin/csh, make goes real
|
||||
# slow and sometimes does the wrong thing.
|
||||
#
|
||||
# this package needs the 'bitstring macros' library, which is
|
||||
# available from me or from the comp.sources.unix archive. if you
|
||||
# put 'bitstring.h' in a non-standard place (i.e., not intuited by
|
||||
# cc(1)), you will have to define INCLUDE to set the include
|
||||
# directory for cc. INCLUDE should be `-Isomethingorother'.
|
||||
#
|
||||
# there's more configuration info in config.h; edit that first!
|
||||
|
||||
TOPSRC = $(shell cd ../../..; pwd)
|
||||
include $(TOPSRC)/target.mk
|
||||
|
||||
CFLAGS += -Werror -I.
|
||||
|
||||
|
||||
DESTROOT = $(DESTDIR)
|
||||
DESTSBIN = $(DESTROOT)/sbin
|
||||
DESTBIN = $(DESTROOT)/bin
|
||||
DESTMAN = $(DESTROOT)/share/man
|
||||
INCLUDE = -I.
|
||||
OPTIM = -O
|
||||
#<<lint flags of choice?>>
|
||||
LINTFLAGS = -hbxa $(INCLUDE)
|
||||
#CC = cc
|
||||
DEFS =
|
||||
INSTALL = install
|
||||
#LDFLAGS = -i
|
||||
#################################### end configurable stuff
|
||||
|
||||
SHELL = /bin/sh
|
||||
#CFLAGS = $(OPTIM) $(INCLUDE) $(DEFS)
|
||||
|
||||
LINT_CRON = cron.c database.c user.c entry.c compat.c \
|
||||
misc.c job.c do_command.c env.c popen.c
|
||||
LINT_CRONTAB = crontab.c misc.c entry.c env.c compat.c
|
||||
CRON_OBJ = cron.o database.o user.o entry.o job.o do_command.o \
|
||||
misc.o env.o popen.o compat.o
|
||||
CRONTAB_OBJ = crontab.o misc.o entry.o env.o compat.o
|
||||
|
||||
all: cron crontab
|
||||
|
||||
lint:
|
||||
lint $(LINTFLAGS) $(LINT_CRON) $(LIBS) \
|
||||
|grep -v "constant argument to NOT" 2>&1
|
||||
lint $(LINTFLAGS) $(LINT_CRONTAB) $(LIBS) \
|
||||
|grep -v "constant argument to NOT" 2>&1
|
||||
|
||||
cron: $(CRON_OBJ)
|
||||
${CC} ${LDFLAGS} -o $@.elf ${CRON_OBJ} ${LIBS}
|
||||
${OBJDUMP} -S $@.elf > $@.dis
|
||||
${SIZE} $@.elf
|
||||
${ELF2AOUT} $@.elf $@
|
||||
|
||||
crontab: $(CRONTAB_OBJ)
|
||||
${CC} ${LDFLAGS} -o $@.elf ${CRONTAB_OBJ} ${LIBS}
|
||||
${OBJDUMP} -S $@.elf > $@.dis
|
||||
${SIZE} $@.elf
|
||||
${ELF2AOUT} $@.elf $@
|
||||
|
||||
install: all
|
||||
cp cron $(DESTSBIN)/
|
||||
cp crontab $(DESTBIN)/
|
||||
sh putman.sh crontab.1 $(DESTMAN)
|
||||
sh putman.sh cron.8 $(DESTMAN)
|
||||
sh putman.sh crontab.5 $(DESTMAN)
|
||||
|
||||
clean:
|
||||
rm -f *.o cron crontab a.out core tags *~ *.elf *.dis
|
||||
|
||||
$(CRON_OBJ) : cron.h compat.h config.h externs.h pathnames.h Makefile
|
||||
$(CRONTAB_OBJ) : cron.h compat.h config.h externs.h pathnames.h Makefile
|
||||
72
src/cmd/cron/README
Executable file
72
src/cmd/cron/README
Executable file
@@ -0,0 +1,72 @@
|
||||
#/* Copyright 1988,1990,1993 by Paul Vixie
|
||||
# * All rights reserved
|
||||
# *
|
||||
# * Distribute freely, except: don't remove my name from the source or
|
||||
# * documentation (don't take credit for my work), mark your changes (don't
|
||||
# * get me blamed for your possible bugs), don't alter or remove this
|
||||
# * notice. May be sold if buildable source is provided to buyer. No
|
||||
# * warrantee of any kind, express or implied, is included with this
|
||||
# * software; use at your own risk, responsibility for damages (if any) to
|
||||
# * anyone resulting from the use of this software rests entirely with the
|
||||
# * user.
|
||||
# *
|
||||
# * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
|
||||
# * I'll try to keep a version up to date. I can be reached as follows:
|
||||
# * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
|
||||
# */
|
||||
|
||||
Vixie Cron V3.0
|
||||
December 27, 1993
|
||||
[V2.2 was some time in 1992]
|
||||
[V2.1 was May 29, 1991]
|
||||
[V2.0 was July 5, 1990]
|
||||
[V2.0-beta was December 9, 1988]
|
||||
[V1.0 was May 6, 1987]
|
||||
Paul Vixie
|
||||
|
||||
This is a version of 'cron' that is known to run on BSD 4.[23] systems. It
|
||||
is functionally based on the SysV cron, which means that each user can have
|
||||
their own crontab file (all crontab files are stored in a read-protected
|
||||
directory, usually /var/cron/tabs). No direct support is provided for
|
||||
'at'; you can continue to run 'atrun' from the crontab as you have been
|
||||
doing. If you don't have atrun (i.e., System V) you are in trouble.
|
||||
|
||||
A messages is logged each time a command is executed; also, the files
|
||||
"allow" and "deny" in /var/cron can be used to control access to the
|
||||
"crontab" command (which installs crontabs). It hasn't been tested on
|
||||
SysV, although some effort has gone into making the port an easy one.
|
||||
|
||||
This is more or less the copyright that USENET contributed software usually
|
||||
has. Since ATT couldn't use this version if they had to freely distribute
|
||||
source, and since I'd love to see them use it, I'll offer some rediculously
|
||||
low license fee just to have them take it. In the unlikely event that they
|
||||
do this, I will continue to support and distribute the pseudo-PD version, so
|
||||
please, don't flame me for wanting my work to see a wider distribution.
|
||||
|
||||
To use this: Sorry, folks, there is no cutesy 'Configure' script. You'll
|
||||
have to go edit a couple of files... So, here's the checklist:
|
||||
|
||||
Read all the FEATURES, INSTALL, and CONVERSION files
|
||||
Edit config.h
|
||||
Edit Makefile
|
||||
(both of these files have instructions inside; note that
|
||||
some things in config.h are definable in Makefile and are
|
||||
therefore surrounded by #ifndef...#endif)
|
||||
'make'
|
||||
'su' and 'make install'
|
||||
(you may have to install the man pages by hand)
|
||||
kill your existing cron process
|
||||
(actually you can run your existing cron if you want, but why?)
|
||||
build new crontabs using /usr/lib/{crontab,crontab.local}
|
||||
(either put them all in "root"'s crontab, or divide it up
|
||||
and rip out all the 'su' commands, collapse the lengthy
|
||||
lists into ranges with steps -- basically, this step is
|
||||
as much work as you want to make it)
|
||||
start up the new cron
|
||||
(must be done as root)
|
||||
watch it. test it with 'crontab -r' and watch the daemon track your
|
||||
changes.
|
||||
if you like it, change your /etc/{rc,rc.local} to use it instead of
|
||||
the old one.
|
||||
|
||||
$Id: README,v 2.3 1993/12/28 08:34:43 vixie Exp $
|
||||
58
src/cmd/cron/README.2BSD
Executable file
58
src/cmd/cron/README.2BSD
Executable file
@@ -0,0 +1,58 @@
|
||||
This is the 2.11BSD port of the 'Vixie cron-3.0 patch1'.
|
||||
|
||||
Updated 1999/8/9: Fix an off by one condition in entry.c that was
|
||||
causing the 'dow' (day of week) bitmap to be overrun by one bit.
|
||||
Properly ifdef'd some rcs/sccs id strings so that they do not get
|
||||
compiled in (data space is at a premium on a 16 bit machine). Fix
|
||||
a bug in bitstring.h (fortunately nothing uses the macro bit_alloc
|
||||
at this time) that was using 'malloc' with two arguments. Do some
|
||||
minor cosmetic cleanup (trailing and extra blank lines, etc). Fix
|
||||
the sigmask handling in cron_pclose() - it was using 'int' as the
|
||||
signal mask type instead of 'long' or more properly 'sigmask_t'.
|
||||
In addition to fixing the sigmask type the code was revised to use
|
||||
modern signal and wait calls. In crontab one of the 'swap_uids'
|
||||
calls should have been 'swap_uids_back' in case "SAVED_UIDS" is
|
||||
supported by the system.
|
||||
|
||||
The files Part01, Part02 and Patch01 were retrieved from
|
||||
ftp://ftp.vix.com/pub/vixie/cron-3.0/
|
||||
|
||||
In an attempt to 'mark my changes' (so Paul Vixie doesn't get
|
||||
blamed for my mistakes) this file (README.2BSD) has been created
|
||||
and the following paragraph added:
|
||||
|
||||
"DO NOT SEND BUG OR PROBLEM REPORTS to Paul Vixie! If 'cron'
|
||||
misbehaves under 2.11BSD use the 'sendbug' program OR look for the
|
||||
mail address in that script and send mail there."
|
||||
|
||||
The major change made was to remove the '#ifdef' statements.
|
||||
The removal of the ifdefs was prompted by several factors:
|
||||
|
||||
2.11BSD defines the symbol 'BSD' as "211". Thus the
|
||||
datestamp based ifdefs ("#if BSD > 930811" for example) were
|
||||
wrong - 2.11 has almost all of the capabilities of later
|
||||
systems.
|
||||
|
||||
2.11BSD has all but one of the capabilities ('setsid') that
|
||||
were conditionalized on "POSIX" . Thus POSIX could not be
|
||||
defined. Neither could POSIX be left undefined because
|
||||
that left out too much. Sigh.
|
||||
|
||||
Conditionalized code is messy to read.
|
||||
|
||||
The probability that this version will ever be ported back
|
||||
to a 'sequent', 'convex' or 'att' system is not at this
|
||||
time distinguishable from 0.
|
||||
|
||||
Other changes include:
|
||||
|
||||
Adding a lot more 'register' declarations (which helps
|
||||
code size - this cron is still over twice as big as the
|
||||
old one).
|
||||
|
||||
Revised the Makefile and 'putman.sh' script to fit in better
|
||||
with 2BSD's philosophy of life.
|
||||
|
||||
Moved a few files into a 'grot' subdirectory to avoid
|
||||
cluttering the main build area. The file "diffs" is the
|
||||
summary of the changes made.
|
||||
168
src/cmd/cron/bitstring.3
Executable file
168
src/cmd/cron/bitstring.3
Executable file
@@ -0,0 +1,168 @@
|
||||
.\" Copyright (c) 1989 The Regents of the University of California.
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" This code is derived from software contributed to Berkeley by
|
||||
.\" Paul Vixie.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms are permitted
|
||||
.\" provided that the above copyright notice and this paragraph are
|
||||
.\" duplicated in all such forms and that any documentation,
|
||||
.\" advertising materials, and other materials related to such
|
||||
.\" distribution and use acknowledge that the software was developed
|
||||
.\" by the University of California, Berkeley. The name of the
|
||||
.\" University may not be used to endorse or promote products derived
|
||||
.\" from this software without specific prior written permission.
|
||||
.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
.\" IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
.\"
|
||||
.\" @(#)bitstring.3 5.1 (Berkeley) 12/13/89
|
||||
.\"
|
||||
.TH BITSTRING 3 "December 13, 1989"
|
||||
.UC 4
|
||||
.SH NAME
|
||||
bit_alloc, bit_clear, bit_decl, bit_ffs, bit_nclear, bit_nset,
|
||||
bit_set, bitstr_size, bit_test \- bit-string manipulation macros
|
||||
.SH SYNOPSIS
|
||||
.ft B
|
||||
.nf
|
||||
#include <bitstring.h>
|
||||
|
||||
name = bit_alloc(nbits)
|
||||
bitstr_t *name;
|
||||
int nbits;
|
||||
|
||||
bit_decl(name, nbits)
|
||||
bitstr_t name;
|
||||
int nbits;
|
||||
|
||||
bit_clear(name, bit)
|
||||
bitstr_t name;
|
||||
int bit;
|
||||
|
||||
bit_ffc(name, nbits, value)
|
||||
bitstr_t name;
|
||||
int nbits, *value;
|
||||
|
||||
bit_ffs(name, nbits, value)
|
||||
bitstr_t name;
|
||||
int nbits, *value;
|
||||
|
||||
bit_nclear(name, start, stop)
|
||||
bitstr_t name;
|
||||
int start, stop;
|
||||
|
||||
bit_nset(name, start, stop)
|
||||
bitstr_t name;
|
||||
int start, stop;
|
||||
|
||||
bit_set(name, bit)
|
||||
bitstr_t name;
|
||||
int bit;
|
||||
|
||||
bitstr_size(nbits)
|
||||
int nbits;
|
||||
|
||||
bit_test(name, bit)
|
||||
bitstr_t name;
|
||||
int bit;
|
||||
.fi
|
||||
.ft R
|
||||
.SH DESCRIPTION
|
||||
These macros operate on strings of bits.
|
||||
.PP
|
||||
.I Bit_alloc
|
||||
returns a pointer of type
|
||||
.I bitstr_t\ *
|
||||
to sufficient space to store
|
||||
.I nbits
|
||||
bits, or NULL if no space is available.
|
||||
.PP
|
||||
.I Bit_decl
|
||||
is a macro for allocating sufficient space to store
|
||||
.I nbits
|
||||
bits on the stack.
|
||||
.PP
|
||||
.I Bitstr_size
|
||||
returns the number of elements of type
|
||||
.I bitstr_t
|
||||
necessary to store
|
||||
.I nbits
|
||||
bits.
|
||||
This is useful for copying bit strings.
|
||||
.PP
|
||||
.I Bit_clear
|
||||
and
|
||||
.I bit_set
|
||||
clear or set the zero-based numbered bit
|
||||
.IR bit ,
|
||||
in the bit string
|
||||
.IR name .
|
||||
.PP
|
||||
.I Bit_nset
|
||||
and
|
||||
.I bit_nclear
|
||||
set or clear the zero-based numbered bits from
|
||||
.I start
|
||||
to
|
||||
.I stop
|
||||
in the bit string
|
||||
.IR name .
|
||||
.PP
|
||||
.I Bit_test
|
||||
evaluates to zero if the zero-based numbered bit
|
||||
.I bit
|
||||
of bit string
|
||||
.I name
|
||||
is set, and non-zero otherwise.
|
||||
.PP
|
||||
.I Bit_ffs
|
||||
sets
|
||||
.I *value
|
||||
to the zero-based number of the first bit set in the array of
|
||||
.I nbits
|
||||
bits referenced by
|
||||
.IR name .
|
||||
If no bits are set,
|
||||
.I *value
|
||||
is set to -1.
|
||||
.PP
|
||||
.I Bit_ffc
|
||||
sets
|
||||
.I *value
|
||||
to the zero-based number of the first bit not set in the array of
|
||||
.I nbits
|
||||
bits referenced by
|
||||
.IR name .
|
||||
If all bits are set,
|
||||
.I value
|
||||
is set to -1.
|
||||
.SH EXAMPLE
|
||||
.nf
|
||||
.in +5
|
||||
#include <limits.h>
|
||||
#include <bitstring.h>
|
||||
|
||||
...
|
||||
#define LPR_BUSY_BIT 0
|
||||
#define LPR_FORMAT_BIT 1
|
||||
#define LPR_DOWNLOAD_BIT 2
|
||||
...
|
||||
#define LPR_AVAILABLE_BIT 9
|
||||
#define LPR_MAX_BITS 10
|
||||
|
||||
make_lpr_available()
|
||||
{
|
||||
bitstr_t bit_decl(bitlist, LPR_MAX_BITS);
|
||||
...
|
||||
bit_nclear(bitlist, 0, LPR_MAX_BITS - 1);
|
||||
...
|
||||
if (!bit_test(bitlist, LPR_BUSY_BIT)) {
|
||||
bit_clear(bitlist, LPR_FORMAT_BIT);
|
||||
bit_clear(bitlist, LPR_DOWNLOAD_BIT);
|
||||
bit_set(bitlist, LPR_AVAILABLE_BIT);
|
||||
}
|
||||
}
|
||||
.fi
|
||||
.SH "SEE ALSO"
|
||||
malloc(3)
|
||||
122
src/cmd/cron/bitstring.h
Executable file
122
src/cmd/cron/bitstring.h
Executable file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (c) 1989 The Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Paul Vixie.
|
||||
*
|
||||
* Redistribution and use in source and binary forms are permitted
|
||||
* provided that the above copyright notice and this paragraph are
|
||||
* duplicated in all such forms and that any documentation,
|
||||
* advertising materials, and other materials related to such
|
||||
* distribution and use acknowledge that the software was developed
|
||||
* by the University of California, Berkeley. The name of the
|
||||
* University may not be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* @(#)bitstring.h 5.2.1 (2.11BSD) 1999/8/5
|
||||
*/
|
||||
|
||||
typedef unsigned char bitstr_t;
|
||||
|
||||
/* internal macros */
|
||||
/* byte of the bitstring bit is in */
|
||||
#define _bit_byte(bit) \
|
||||
((bit) >> 3)
|
||||
|
||||
/* mask for the bit within its byte */
|
||||
#define _bit_mask(bit) \
|
||||
(1 << ((bit)&0x7))
|
||||
|
||||
/* external macros */
|
||||
/* bytes in a bitstring of nbits bits */
|
||||
#define bitstr_size(nbits) \
|
||||
((((nbits) - 1) >> 3) + 1)
|
||||
|
||||
/* allocate a bitstring */
|
||||
#define bit_alloc(nbits) \
|
||||
(bitstr_t *)calloc(1, \
|
||||
(unsigned int)bitstr_size(nbits) * sizeof(bitstr_t))
|
||||
|
||||
/* allocate a bitstring on the stack */
|
||||
#define bit_decl(name, nbits) \
|
||||
(name)[bitstr_size(nbits)]
|
||||
|
||||
/* is bit N of bitstring name set? */
|
||||
#define bit_test(name, bit) \
|
||||
((name)[_bit_byte(bit)] & _bit_mask(bit))
|
||||
|
||||
/* set bit N of bitstring name */
|
||||
#define bit_set(name, bit) \
|
||||
(name)[_bit_byte(bit)] |= _bit_mask(bit)
|
||||
|
||||
/* clear bit N of bitstring name */
|
||||
#define bit_clear(name, bit) \
|
||||
(name)[_bit_byte(bit)] &= ~_bit_mask(bit)
|
||||
|
||||
/* clear bits start ... stop in bitstring */
|
||||
#define bit_nclear(name, start, stop) { \
|
||||
register bitstr_t *_name = name; \
|
||||
register int _start = start, _stop = stop; \
|
||||
register int _startbyte = _bit_byte(_start); \
|
||||
register int _stopbyte = _bit_byte(_stop); \
|
||||
if (_startbyte == _stopbyte) { \
|
||||
_name[_startbyte] &= ((0xff >> (8 - (_start&0x7))) | \
|
||||
(0xff << ((_stop&0x7) + 1))); \
|
||||
} else { \
|
||||
_name[_startbyte] &= 0xff >> (8 - (_start&0x7)); \
|
||||
while (++_startbyte < _stopbyte) \
|
||||
_name[_startbyte] = 0; \
|
||||
_name[_stopbyte] &= 0xff << ((_stop&0x7) + 1); \
|
||||
} \
|
||||
}
|
||||
|
||||
/* set bits start ... stop in bitstring */
|
||||
#define bit_nset(name, start, stop) { \
|
||||
register bitstr_t *_name = name; \
|
||||
register int _start = start, _stop = stop; \
|
||||
register int _startbyte = _bit_byte(_start); \
|
||||
register int _stopbyte = _bit_byte(_stop); \
|
||||
if (_startbyte == _stopbyte) { \
|
||||
_name[_startbyte] |= ((0xff << (_start&0x7)) & \
|
||||
(0xff >> (7 - (_stop&0x7)))); \
|
||||
} else { \
|
||||
_name[_startbyte] |= 0xff << ((_start)&0x7); \
|
||||
while (++_startbyte < _stopbyte) \
|
||||
_name[_startbyte] = 0xff; \
|
||||
_name[_stopbyte] |= 0xff >> (7 - (_stop&0x7)); \
|
||||
} \
|
||||
}
|
||||
|
||||
/* find first bit clear in name */
|
||||
#define bit_ffc(name, nbits, value) { \
|
||||
register bitstr_t *_name = name; \
|
||||
register int _byte, _nbits = nbits; \
|
||||
register int _stopbyte = _bit_byte(_nbits), _value = -1; \
|
||||
for (_byte = 0; _byte <= _stopbyte; ++_byte) \
|
||||
if (_name[_byte] != 0xff) { \
|
||||
_value = _byte << 3; \
|
||||
for (_stopbyte = _name[_byte]; (_stopbyte&0x1); \
|
||||
++_value, _stopbyte >>= 1); \
|
||||
break; \
|
||||
} \
|
||||
*(value) = _value; \
|
||||
}
|
||||
|
||||
/* find first bit set in name */
|
||||
#define bit_ffs(name, nbits, value) { \
|
||||
register bitstr_t *_name = name; \
|
||||
register int _byte, _nbits = nbits; \
|
||||
register int _stopbyte = _bit_byte(_nbits), _value = -1; \
|
||||
for (_byte = 0; _byte <= _stopbyte; ++_byte) \
|
||||
if (_name[_byte]) { \
|
||||
_value = _byte << 3; \
|
||||
for (_stopbyte = _name[_byte]; !(_stopbyte&0x1); \
|
||||
++_value, _stopbyte >>= 1); \
|
||||
break; \
|
||||
} \
|
||||
*(value) = _value; \
|
||||
}
|
||||
58
src/cmd/cron/compat.c
Executable file
58
src/cmd/cron/compat.c
Executable file
@@ -0,0 +1,58 @@
|
||||
/* Copyright 1988,1990,1993,1994 by Paul Vixie
|
||||
* All rights reserved
|
||||
*
|
||||
* Distribute freely, except: don't remove my name from the source or
|
||||
* documentation (don't take credit for my work), mark your changes (don't
|
||||
* get me blamed for your possible bugs), don't alter or remove this
|
||||
* notice. May be sold if buildable source is provided to buyer. No
|
||||
* warrantee of any kind, express or implied, is included with this
|
||||
* software; use at your own risk, responsibility for damages (if any) to
|
||||
* anyone resulting from the use of this software rests entirely with the
|
||||
* user.
|
||||
*
|
||||
* Send bug reports, bug fixes, enhancements, requests, flames, etc., and
|
||||
* I'll try to keep a version up to date. I can be reached as follows:
|
||||
* Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
|
||||
*/
|
||||
|
||||
#if !defined(lint) && !defined(LINT)
|
||||
static char rcsid[] = "$Id: compat.c,v 1.6 1994/01/15 20:43:43 vixie Exp $";
|
||||
#endif
|
||||
|
||||
/* vix 30dec93 [broke this out of misc.c - see RCS log for history]
|
||||
* vix 15jan87 [added TIOCNOTTY, thanks csg@pyramid]
|
||||
*/
|
||||
|
||||
|
||||
#include "cron.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
/*
|
||||
* Ripped off from daemon(3) - differences are this sets the process group
|
||||
* and does not fork (because that has been done already).
|
||||
*/
|
||||
int
|
||||
setsid()
|
||||
{
|
||||
int newpgrp;
|
||||
register int fd;
|
||||
|
||||
newpgrp = setpgrp(0, getpid());
|
||||
if ((fd = open(_PATH_TTY, 2)) >= 0)
|
||||
{
|
||||
(void) ioctl(fd, TIOCNOTTY, (char*)0);
|
||||
(void) close(fd);
|
||||
}
|
||||
if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1)
|
||||
{
|
||||
(void)dup2(fd, 0);
|
||||
(void)dup2(fd, 1);
|
||||
(void)dup2(fd, 2);
|
||||
if (fd > 2)
|
||||
(void)close(fd);
|
||||
}
|
||||
return newpgrp;
|
||||
}
|
||||
32
src/cmd/cron/compat.h
Executable file
32
src/cmd/cron/compat.h
Executable file
@@ -0,0 +1,32 @@
|
||||
/* Copyright 1993,1994 by Paul Vixie
|
||||
* All rights reserved
|
||||
*
|
||||
* Distribute freely, except: don't remove my name from the source or
|
||||
* documentation (don't take credit for my work), mark your changes (don't
|
||||
* get me blamed for your possible bugs), don't alter or remove this
|
||||
* notice. May be sold if buildable source is provided to buyer. No
|
||||
* warrantee of any kind, express or implied, is included with this
|
||||
* software; use at your own risk, responsibility for damages (if any) to
|
||||
* anyone resulting from the use of this software rests entirely with the
|
||||
* user.
|
||||
*
|
||||
* Send bug reports, bug fixes, enhancements, requests, flames, etc., and
|
||||
* I'll try to keep a version up to date. I can be reached as follows:
|
||||
* Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
|
||||
*/
|
||||
|
||||
/*
|
||||
* $Id: compat.h,v 1.8 1994/01/15 20:43:43 vixie Exp $
|
||||
*/
|
||||
|
||||
#ifndef __P
|
||||
#define __P(x) ()
|
||||
#define const
|
||||
#endif
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
#define WAIT_T int
|
||||
//union wait
|
||||
#define PID_T pid_t
|
||||
#define TIME_T time_t
|
||||
86
src/cmd/cron/config.h
Executable file
86
src/cmd/cron/config.h
Executable file
@@ -0,0 +1,86 @@
|
||||
/* Copyright 1988,1990,1993,1994 by Paul Vixie
|
||||
* All rights reserved
|
||||
*
|
||||
* Distribute freely, except: don't remove my name from the source or
|
||||
* documentation (don't take credit for my work), mark your changes (don't
|
||||
* get me blamed for your possible bugs), don't alter or remove this
|
||||
* notice. May be sold if buildable source is provided to buyer. No
|
||||
* warrantee of any kind, express or implied, is included with this
|
||||
* software; use at your own risk, responsibility for damages (if any) to
|
||||
* anyone resulting from the use of this software rests entirely with the
|
||||
* user.
|
||||
*
|
||||
* Send bug reports, bug fixes, enhancements, requests, flames, etc., and
|
||||
* I'll try to keep a version up to date. I can be reached as follows:
|
||||
* Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
|
||||
*/
|
||||
|
||||
/* config.h - configurables for Vixie Cron
|
||||
*
|
||||
* $Id: config.h,v 2.6 1994/01/15 20:43:43 vixie Exp $
|
||||
*/
|
||||
|
||||
#if !defined(_PATH_SENDMAIL)
|
||||
# define _PATH_SENDMAIL "/usr/lib/sendmail"
|
||||
#endif /*SENDMAIL*/
|
||||
|
||||
/*
|
||||
* these are site-dependent
|
||||
*/
|
||||
|
||||
#ifndef DEBUGGING
|
||||
#define DEBUGGING 1 /* 1 or 0 -- do you want debugging code built in? */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* choose one of these MAILCMD commands. I use
|
||||
* /bin/mail for speed; it makes biff bark but doesn't
|
||||
* do aliasing. /usr/lib/sendmail does aliasing but is
|
||||
* a hog for short messages. aliasing is not needed
|
||||
* if you make use of the MAILTO= feature in crontabs.
|
||||
* (hint: MAILTO= was added for this reason).
|
||||
*/
|
||||
|
||||
#define MAILCMD _PATH_SENDMAIL /*-*/
|
||||
#define MAILARGS "%s -FCronDaemon -odi -oem -or0s %s" /*-*/
|
||||
/* -Fx = set full-name of sender
|
||||
* -odi = Option Deliverymode Interactive
|
||||
* -oem = Option Errors Mailedtosender
|
||||
* -or0s = Option Readtimeout -- don't time out
|
||||
*/
|
||||
|
||||
/* #define MAILCMD "/bin/mail" /*-*/
|
||||
/* #define MAILARGS "%s -d %s" /*-*/
|
||||
/* -d = undocumented but common flag: deliver locally?
|
||||
*/
|
||||
|
||||
/* #define MAILCMD "/usr/mmdf/bin/submit" /*-*/
|
||||
/* #define MAILARGS "%s -mlrxto %s" /*-*/
|
||||
|
||||
/* #define MAIL_DATE /*-*/
|
||||
/* should we include an ersatz Date: header in
|
||||
* generated mail? if you are using sendmail
|
||||
* for MAILCMD, it is better to let sendmail
|
||||
* generate the Date: header.
|
||||
*/
|
||||
|
||||
/* if ALLOW_FILE and DENY_FILE are not defined or are
|
||||
* defined but neither exists, should crontab(1) be
|
||||
* usable only by root?
|
||||
*/
|
||||
/*#define ALLOW_ONLY_ROOT /*-*/
|
||||
|
||||
/* if you want to use syslog(3) instead of appending
|
||||
* to CRONDIR/LOG_FILE (/var/cron/log, e.g.), define
|
||||
* SYSLOG here. Note that quite a bit of logging
|
||||
* info is written, and that you probably don't want
|
||||
* to use this on 4.2bsd since everything goes in
|
||||
* /usr/spool/mqueue/syslog. On 4.[34]bsd you can
|
||||
* tell /etc/syslog.conf to send cron's logging to
|
||||
* a separate file.
|
||||
*
|
||||
* Note that if this and LOG_FILE in "pathnames.h"
|
||||
* are both defined, then logging will go to both
|
||||
* places.
|
||||
*/
|
||||
#define SYSLOG /*-*/
|
||||
61
src/cmd/cron/cron.8
Executable file
61
src/cmd/cron/cron.8
Executable file
@@ -0,0 +1,61 @@
|
||||
.\"/* Copyright 1988,1990,1993 by Paul Vixie
|
||||
.\" * All rights reserved
|
||||
.\" *
|
||||
.\" * Distribute freely, except: don't remove my name from the source or
|
||||
.\" * documentation (don't take credit for my work), mark your changes (don't
|
||||
.\" * get me blamed for your possible bugs), don't alter or remove this
|
||||
.\" * notice. May be sold if buildable source is provided to buyer. No
|
||||
.\" * warrantee of any kind, express or implied, is included with this
|
||||
.\" * software; use at your own risk, responsibility for damages (if any) to
|
||||
.\" * anyone resulting from the use of this software rests entirely with the
|
||||
.\" * user.
|
||||
.\" *
|
||||
.\" * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
|
||||
.\" * I'll try to keep a version up to date. I can be reached as follows:
|
||||
.\" * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
|
||||
.\" */
|
||||
.\"
|
||||
.\" $Id: cron.8,v 2.2 1993/12/28 08:34:43 vixie Exp $
|
||||
.\"
|
||||
.TH CRON 8 "20 December 1993"
|
||||
.UC 4
|
||||
.SH NAME
|
||||
cron \- daemon to execute scheduled commands (Vixie Cron)
|
||||
.SH SYNOPSIS
|
||||
cron
|
||||
.SH DESCRIPTION
|
||||
.I Cron
|
||||
should be started from /etc/rc or /etc/rc.local. It will return immediately,
|
||||
so you don't need to start it with '&'.
|
||||
.PP
|
||||
.I Cron
|
||||
searches /var/cron/tabs for crontab files which are named after accounts in
|
||||
/etc/passwd; crontabs found are loaded into memory.
|
||||
.I Cron
|
||||
also searches for /etc/crontab which is in a different format (see
|
||||
.IR crontab(5)).
|
||||
.I Cron
|
||||
then wakes up every minute, examining all stored crontabs, checking each
|
||||
command to see if it should be run in the current minute. When executing
|
||||
commands, any output is mailed to the owner of the crontab (or to the user
|
||||
named in the MAILTO environment variable in the crontab, if such exists).
|
||||
.PP
|
||||
Additionally,
|
||||
.I cron
|
||||
checks each minute to see if its spool directory's modtime (or the modtime
|
||||
on
|
||||
.IR /etc/crontab)
|
||||
has changed, and if it has,
|
||||
.I cron
|
||||
will then examine the modtime on all crontabs and reload those which have
|
||||
changed. Thus
|
||||
.I cron
|
||||
need not be restarted whenever a crontab file is modified. Note that the
|
||||
.IR Crontab (1)
|
||||
command updates the modtime of the spool directory whenever it changes a
|
||||
crontab.
|
||||
.SH "SEE ALSO"
|
||||
crontab(1), crontab(5)
|
||||
.SH AUTHOR
|
||||
.nf
|
||||
Paul Vixie <paul@vix.com>
|
||||
277
src/cmd/cron/cron.c
Executable file
277
src/cmd/cron/cron.c
Executable file
@@ -0,0 +1,277 @@
|
||||
/* Copyright 1988,1990,1993,1994 by Paul Vixie
|
||||
* All rights reserved
|
||||
*
|
||||
* Distribute freely, except: don't remove my name from the source or
|
||||
* documentation (don't take credit for my work), mark your changes (don't
|
||||
* get me blamed for your possible bugs), don't alter or remove this
|
||||
* notice. May be sold if buildable source is provided to buyer. No
|
||||
* warrantee of any kind, express or implied, is included with this
|
||||
* software; use at your own risk, responsibility for damages (if any) to
|
||||
* anyone resulting from the use of this software rests entirely with the
|
||||
* user.
|
||||
*
|
||||
* Send bug reports, bug fixes, enhancements, requests, flames, etc., and
|
||||
* I'll try to keep a version up to date. I can be reached as follows:
|
||||
* Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
|
||||
*/
|
||||
|
||||
#if !defined(lint) && defined(DOSCCS)
|
||||
static char sccsid[] = "@(#)cron.c 2.11.1 (2.11BSD) 1999/08/05";
|
||||
#endif
|
||||
|
||||
#define MAIN_PROGRAM
|
||||
|
||||
#include "cron.h"
|
||||
#include <sys/signal.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
static void usage __P((void)),
|
||||
run_reboot_jobs __P((cron_db *)),
|
||||
cron_tick __P((cron_db *)),
|
||||
cron_sync __P((void)),
|
||||
cron_sleep __P((void)),
|
||||
sigchld_handler __P((int)),
|
||||
sighup_handler __P((int)),
|
||||
parse_args __P((int c, char *v[]));
|
||||
|
||||
|
||||
static void
|
||||
usage() {
|
||||
fprintf(stderr, "usage: %s [-x debugflag[,...]]\n", ProgramName);
|
||||
exit(ERROR_EXIT);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(argc, argv)
|
||||
int argc;
|
||||
char *argv[];
|
||||
{
|
||||
cron_db database;
|
||||
|
||||
ProgramName = argv[0];
|
||||
|
||||
setlinebuf(stdout);
|
||||
setlinebuf(stderr);
|
||||
|
||||
parse_args(argc, argv);
|
||||
|
||||
(void) signal(SIGCHLD, sigchld_handler);
|
||||
(void) signal(SIGHUP, sighup_handler);
|
||||
|
||||
acquire_daemonlock(0);
|
||||
set_cron_uid();
|
||||
set_cron_cwd();
|
||||
|
||||
setenv("PATH", _PATH_DEFPATH, 1);
|
||||
|
||||
/* if there are no debug flags turned on, fork as a daemon should.
|
||||
*/
|
||||
# if DEBUGGING
|
||||
if (DebugFlags) {
|
||||
# else
|
||||
if (0) {
|
||||
# endif
|
||||
(void) fprintf(stderr, "[%d] cron started\n", getpid());
|
||||
} else {
|
||||
switch (fork()) {
|
||||
case -1:
|
||||
log_it("CRON",getpid(),"DEATH","can't fork");
|
||||
exit(0);
|
||||
break;
|
||||
case 0:
|
||||
/* child process */
|
||||
log_it("CRON",getpid(),"STARTUP","fork ok");
|
||||
(void) setsid();
|
||||
break;
|
||||
default:
|
||||
/* parent process should just die */
|
||||
_exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
acquire_daemonlock(0);
|
||||
database.head = NULL;
|
||||
database.tail = NULL;
|
||||
database.mtime = (time_t) 0;
|
||||
load_database(&database);
|
||||
run_reboot_jobs(&database);
|
||||
cron_sync();
|
||||
while (TRUE) {
|
||||
# if DEBUGGING
|
||||
if (!(DebugFlags & DTEST))
|
||||
# endif /*DEBUGGING*/
|
||||
cron_sleep();
|
||||
|
||||
load_database(&database);
|
||||
|
||||
/* do this iteration
|
||||
*/
|
||||
cron_tick(&database);
|
||||
|
||||
/* sleep 1 minute
|
||||
*/
|
||||
TargetTime += 60;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
run_reboot_jobs(db)
|
||||
cron_db *db;
|
||||
{
|
||||
register user *u;
|
||||
register entry *e;
|
||||
|
||||
for (u = db->head; u != NULL; u = u->next) {
|
||||
for (e = u->crontab; e != NULL; e = e->next) {
|
||||
if (e->flags & WHEN_REBOOT) {
|
||||
job_add(e, u);
|
||||
}
|
||||
}
|
||||
}
|
||||
(void) job_runqueue();
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
cron_tick(db)
|
||||
cron_db *db;
|
||||
{
|
||||
register struct tm *tm = localtime(&TargetTime);
|
||||
register int minute, hour, dom, month, dow;
|
||||
register user *u;
|
||||
register entry *e;
|
||||
|
||||
/* make 0-based values out of these so we can use them as indicies
|
||||
*/
|
||||
minute = tm->tm_min -FIRST_MINUTE;
|
||||
hour = tm->tm_hour -FIRST_HOUR;
|
||||
dom = tm->tm_mday -FIRST_DOM;
|
||||
month = tm->tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH;
|
||||
dow = tm->tm_wday -FIRST_DOW;
|
||||
|
||||
Debug(DSCH, ("[%d] tick(%d,%d,%d,%d,%d)\n",
|
||||
getpid(), minute, hour, dom, month, dow))
|
||||
|
||||
/* the dom/dow situation is odd. '* * 1,15 * Sun' will run on the
|
||||
* first and fifteenth AND every Sunday; '* * * * Sun' will run *only*
|
||||
* on Sundays; '* * 1,15 * *' will run *only* the 1st and 15th. this
|
||||
* is why we keep 'e->dow_star' and 'e->dom_star'. yes, it's bizarre.
|
||||
* like many bizarre things, it's the standard.
|
||||
*/
|
||||
for (u = db->head; u != NULL; u = u->next) {
|
||||
for (e = u->crontab; e != NULL; e = e->next) {
|
||||
Debug(DSCH|DEXT, ("user [%s:%d:%d:...] cmd=\"%s\"\n",
|
||||
env_get("LOGNAME", e->envp),
|
||||
e->uid, e->gid, e->cmd))
|
||||
if (bit_test(e->minute, minute)
|
||||
&& bit_test(e->hour, hour)
|
||||
&& bit_test(e->month, month)
|
||||
&& ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR))
|
||||
? (bit_test(e->dow,dow) && bit_test(e->dom,dom))
|
||||
: (bit_test(e->dow,dow) || bit_test(e->dom,dom))
|
||||
)
|
||||
) {
|
||||
job_add(e, u);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* the task here is to figure out how long it's going to be until :00 of the
|
||||
* following minute and initialize TargetTime to this value. TargetTime
|
||||
* will subsequently slide 60 seconds at a time, with correction applied
|
||||
* implicitly in cron_sleep(). it would be nice to let cron execute in
|
||||
* the "current minute" before going to sleep, but by restarting cron you
|
||||
* could then get it to execute a given minute's jobs more than once.
|
||||
* instead we have the chance of missing a minute's jobs completely, but
|
||||
* that's something sysadmin's know to expect what with crashing computers..
|
||||
*/
|
||||
static void
|
||||
cron_sync() {
|
||||
register struct tm *tm;
|
||||
|
||||
TargetTime = time((time_t*)0);
|
||||
tm = localtime(&TargetTime);
|
||||
TargetTime += (60 - tm->tm_sec);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
cron_sleep() {
|
||||
register int seconds_to_wait;
|
||||
|
||||
do {
|
||||
seconds_to_wait = (int) (TargetTime - time((time_t*)0));
|
||||
Debug(DSCH, ("[%d] TargetTime=%ld, sec-to-wait=%d\n",
|
||||
getpid(), TargetTime, seconds_to_wait))
|
||||
|
||||
/* if we intend to sleep, this means that it's finally
|
||||
* time to empty the job queue (execute it).
|
||||
*
|
||||
* if we run any jobs, we'll probably screw up our timing,
|
||||
* so go recompute.
|
||||
*
|
||||
* note that we depend here on the left-to-right nature
|
||||
* of &&, and the short-circuiting.
|
||||
*/
|
||||
} while (seconds_to_wait > 0 && job_runqueue());
|
||||
|
||||
while (seconds_to_wait > 0) {
|
||||
Debug(DSCH, ("[%d] sleeping for %d seconds\n",
|
||||
getpid(), seconds_to_wait))
|
||||
seconds_to_wait = (int) sleep((unsigned int) seconds_to_wait);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
sigchld_handler(x) {
|
||||
WAIT_T waiter;
|
||||
PID_T pid;
|
||||
|
||||
for (;;) {
|
||||
pid = waitpid(-1, &waiter, WNOHANG);
|
||||
switch (pid) {
|
||||
case -1:
|
||||
Debug(DPROC,
|
||||
("[%d] sigchld...no children\n", getpid()))
|
||||
return;
|
||||
case 0:
|
||||
Debug(DPROC,
|
||||
("[%d] sigchld...no dead kids\n", getpid()))
|
||||
return;
|
||||
default:
|
||||
Debug(DPROC,
|
||||
("[%d] sigchld...pid #%d died, stat=%d\n",
|
||||
getpid(), pid, WEXITSTATUS(waiter)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sighup_handler(x) {
|
||||
log_close();
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
parse_args(argc, argv)
|
||||
int argc;
|
||||
char *argv[];
|
||||
{
|
||||
int argch;
|
||||
|
||||
while (EOF != (argch = getopt(argc, argv, "x:"))) {
|
||||
switch (argch) {
|
||||
default:
|
||||
usage();
|
||||
case 'x':
|
||||
if (!set_debug_flags(optarg))
|
||||
usage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
275
src/cmd/cron/cron.h
Executable file
275
src/cmd/cron/cron.h
Executable file
@@ -0,0 +1,275 @@
|
||||
/* Copyright 1988,1990,1993,1994 by Paul Vixie
|
||||
* All rights reserved
|
||||
*
|
||||
* Distribute freely, except: don't remove my name from the source or
|
||||
* documentation (don't take credit for my work), mark your changes (don't
|
||||
* get me blamed for your possible bugs), don't alter or remove this
|
||||
* notice. May be sold if buildable source is provided to buyer. No
|
||||
* warrantee of any kind, express or implied, is included with this
|
||||
* software; use at your own risk, responsibility for damages (if any) to
|
||||
* anyone resulting from the use of this software rests entirely with the
|
||||
* user.
|
||||
*
|
||||
* Send bug reports, bug fixes, enhancements, requests, flames, etc., and
|
||||
* I'll try to keep a version up to date. I can be reached as follows:
|
||||
* Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
|
||||
*/
|
||||
|
||||
/* cron.h - header for vixie's cron
|
||||
*
|
||||
* $Id: cron.h,v 2.10 1994/01/15 20:43:43 vixie Exp $
|
||||
*
|
||||
* vix 14nov88 [rest of log is in RCS]
|
||||
* vix 14jan87 [0 or 7 can be sunday; thanks, mwm@berkeley]
|
||||
* vix 30dec86 [written]
|
||||
*/
|
||||
|
||||
/* reorder these #include's at your peril */
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
#include "compat.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <bitstring.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "pathnames.h"
|
||||
#include "config.h"
|
||||
#include "externs.h"
|
||||
|
||||
/* these are really immutable, and are
|
||||
* defined for symbolic convenience only
|
||||
* TRUE, FALSE, and ERR must be distinct
|
||||
* ERR must be < OK.
|
||||
*/
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
/* system calls return this on success */
|
||||
#define OK 0
|
||||
/* or this on error */
|
||||
#define ERR (-1)
|
||||
|
||||
/* turn this on to get '-x' code */
|
||||
#ifndef DEBUGGING
|
||||
#define DEBUGGING FALSE
|
||||
#endif
|
||||
|
||||
#define READ_PIPE 0 /* which end of a pipe pair do you read? */
|
||||
#define WRITE_PIPE 1 /* or write to? */
|
||||
#define STDIN 0 /* what is stdin's file descriptor? */
|
||||
#define STDOUT 1 /* stdout's? */
|
||||
#define STDERR 2 /* stderr's? */
|
||||
#define ERROR_EXIT 1 /* exit() with this will scare the shell */
|
||||
#define OK_EXIT 0 /* exit() with this is considered 'normal' */
|
||||
#define MAX_FNAME 100 /* max length of internally generated fn */
|
||||
#define MAX_COMMAND 1000 /* max length of internally generated cmd */
|
||||
#define MAX_ENVSTR 1000 /* max length of envvar=value\0 strings */
|
||||
#define MAX_TEMPSTR 100 /* obvious */
|
||||
#define MAX_UNAME 20 /* max length of username, should be overkill */
|
||||
#define ROOT_UID 0 /* don't change this, it really must be root */
|
||||
#define ROOT_USER "root" /* ditto */
|
||||
|
||||
/* NOTE: these correspond to DebugFlagNames,
|
||||
* defined below.
|
||||
*/
|
||||
#define DEXT 0x0001 /* extend flag for other debug masks */
|
||||
#define DSCH 0x0002 /* scheduling debug mask */
|
||||
#define DPROC 0x0004 /* process control debug mask */
|
||||
#define DPARS 0x0008 /* parsing debug mask */
|
||||
#define DLOAD 0x0010 /* database loading debug mask */
|
||||
#define DMISC 0x0020 /* misc debug mask */
|
||||
#define DTEST 0x0040 /* test mode: don't execute any commands */
|
||||
#define DBIT 0x0080 /* bit twiddling shown (long) */
|
||||
|
||||
#define CRON_TAB(u) "%s/%s", SPOOL_DIR, u
|
||||
#define REG register
|
||||
#define PPC_NULL ((char **)NULL)
|
||||
|
||||
#ifndef MAXHOSTNAMELEN
|
||||
#define MAXHOSTNAMELEN 64
|
||||
#endif
|
||||
|
||||
#define Skip_Blanks(c, f) \
|
||||
while (c == '\t' || c == ' ') \
|
||||
c = get_char(f);
|
||||
|
||||
#define Skip_Nonblanks(c, f) \
|
||||
while (c!='\t' && c!=' ' && c!='\n' && c != EOF) \
|
||||
c = get_char(f);
|
||||
|
||||
#define Skip_Line(c, f) \
|
||||
do {c = get_char(f);} while (c != '\n' && c != EOF);
|
||||
|
||||
#if DEBUGGING
|
||||
# define Debug(mask, message) \
|
||||
if ( (DebugFlags & (mask) ) == (mask) ) \
|
||||
printf message;
|
||||
#else /* !DEBUGGING */
|
||||
# define Debug(mask, message) \
|
||||
;
|
||||
#endif /* DEBUGGING */
|
||||
|
||||
#define Set_LineNum(ln) {Debug(DPARS|DEXT,("linenum=%d\n",ln)); \
|
||||
LineNumber = ln; \
|
||||
}
|
||||
|
||||
#define FIRST_MINUTE 0
|
||||
#define LAST_MINUTE 59
|
||||
#define MINUTE_COUNT (LAST_MINUTE - FIRST_MINUTE + 1)
|
||||
|
||||
#define FIRST_HOUR 0
|
||||
#define LAST_HOUR 23
|
||||
#define HOUR_COUNT (LAST_HOUR - FIRST_HOUR + 1)
|
||||
|
||||
#define FIRST_DOM 1
|
||||
#define LAST_DOM 31
|
||||
#define DOM_COUNT (LAST_DOM - FIRST_DOM + 1)
|
||||
|
||||
#define FIRST_MONTH 1
|
||||
#define LAST_MONTH 12
|
||||
#define MONTH_COUNT (LAST_MONTH - FIRST_MONTH + 1)
|
||||
|
||||
/* note on DOW: 0 and 7 are both Sunday, for compatibility reasons. */
|
||||
#define FIRST_DOW 0
|
||||
#define LAST_DOW 7
|
||||
#define DOW_COUNT (LAST_DOW - FIRST_DOW + 1)
|
||||
|
||||
/* each user's crontab will be held as a list of
|
||||
* the following structure.
|
||||
*
|
||||
* These are the cron commands.
|
||||
*/
|
||||
|
||||
typedef struct _entry {
|
||||
struct _entry *next;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
char **envp;
|
||||
char *cmd;
|
||||
bitstr_t bit_decl(minute, MINUTE_COUNT);
|
||||
bitstr_t bit_decl(hour, HOUR_COUNT);
|
||||
bitstr_t bit_decl(dom, DOM_COUNT);
|
||||
bitstr_t bit_decl(month, MONTH_COUNT);
|
||||
bitstr_t bit_decl(dow, DOW_COUNT);
|
||||
int flags;
|
||||
#define DOM_STAR 0x01
|
||||
#define DOW_STAR 0x02
|
||||
#define WHEN_REBOOT 0x04
|
||||
} entry;
|
||||
|
||||
/* the crontab database will be a list of the
|
||||
* following structure, one element per user
|
||||
* plus one for the system.
|
||||
*
|
||||
* These are the crontabs.
|
||||
*/
|
||||
|
||||
typedef struct _user {
|
||||
struct _user *next, *prev; /* links */
|
||||
char *name;
|
||||
time_t mtime; /* last modtime of crontab */
|
||||
entry *crontab; /* this person's crontab */
|
||||
} user;
|
||||
|
||||
typedef struct _cron_db {
|
||||
user *head, *tail; /* links */
|
||||
time_t mtime; /* last modtime on spooldir */
|
||||
} cron_db;
|
||||
|
||||
|
||||
void set_cron_uid __P((void)),
|
||||
set_cron_cwd __P((void)),
|
||||
load_database __P((cron_db *)),
|
||||
open_logfile __P((void)),
|
||||
sigpipe_func __P((void)),
|
||||
job_add __P((entry *, user *)),
|
||||
do_command __P((entry *, user *)),
|
||||
link_user __P((cron_db *, user *)),
|
||||
unlink_user __P((cron_db *, user *)),
|
||||
free_user __P((user *)),
|
||||
env_free __P((char **)),
|
||||
unget_char __P((int, FILE *)),
|
||||
free_entry __P((entry *)),
|
||||
acquire_daemonlock __P((int)),
|
||||
skip_comments __P((FILE *)),
|
||||
log_it __P((char *, int, char *, char *)),
|
||||
log_close __P((void));
|
||||
|
||||
int job_runqueue __P((void)),
|
||||
set_debug_flags __P((char *)),
|
||||
get_char __P((FILE *)),
|
||||
get_string __P((char *, int, FILE *, char *)),
|
||||
swap_uids __P((void)),
|
||||
load_env __P((char *, FILE *)),
|
||||
cron_pclose __P((FILE *)),
|
||||
strcmp_until __P((char *, char *, int)),
|
||||
allowed __P((char *)),
|
||||
strdtb __P((char *));
|
||||
|
||||
char *env_get __P((char *, char **)),
|
||||
*arpadate __P((time_t *)),
|
||||
*mkprints __P((unsigned char *, unsigned int)),
|
||||
*first_word __P((char *, char *)),
|
||||
**env_init __P((void)),
|
||||
**env_copy __P((char **)),
|
||||
**env_set __P((char **, char *));
|
||||
|
||||
user *load_user __P((int, struct passwd *, char *)),
|
||||
*find_user __P((cron_db *, char *));
|
||||
|
||||
entry *load_entry __P((FILE *, void (*)(),
|
||||
struct passwd *, char **));
|
||||
|
||||
FILE *cron_popen __P((char *, char *));
|
||||
|
||||
|
||||
/* in the C tradition, we only create
|
||||
* variables for the main program, just
|
||||
* extern them elsewhere.
|
||||
*/
|
||||
|
||||
#ifdef MAIN_PROGRAM
|
||||
# if !defined(LINT) && !defined(lint)
|
||||
char *copyright[] = {
|
||||
"@(#) Copyright 1988,1989,1990,1993,1994 by Paul Vixie",
|
||||
"@(#) All rights reserved"
|
||||
};
|
||||
# endif
|
||||
|
||||
char *MonthNames[] = {
|
||||
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
|
||||
NULL
|
||||
};
|
||||
|
||||
char *DowNames[] = {
|
||||
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun",
|
||||
NULL
|
||||
};
|
||||
|
||||
char *ProgramName;
|
||||
int LineNumber;
|
||||
time_t TargetTime;
|
||||
|
||||
# if DEBUGGING
|
||||
int DebugFlags;
|
||||
char *DebugFlagNames[] = { /* sync with #defines */
|
||||
"ext", "sch", "proc", "pars", "load", "misc", "test", "bit",
|
||||
NULL /* NULL must be last element */
|
||||
};
|
||||
# endif /* DEBUGGING */
|
||||
#else /*MAIN_PROGRAM*/
|
||||
extern char *copyright[],
|
||||
*MonthNames[],
|
||||
*DowNames[],
|
||||
*ProgramName;
|
||||
extern int LineNumber;
|
||||
extern time_t TargetTime;
|
||||
# if DEBUGGING
|
||||
extern int DebugFlags;
|
||||
extern char *DebugFlagNames[];
|
||||
# endif /* DEBUGGING */
|
||||
#endif /*MAIN_PROGRAM*/
|
||||
100
src/cmd/cron/crontab.1
Executable file
100
src/cmd/cron/crontab.1
Executable file
@@ -0,0 +1,100 @@
|
||||
.\"/* Copyright 1988,1990,1993 by Paul Vixie
|
||||
.\" * All rights reserved
|
||||
.\" *
|
||||
.\" * Distribute freely, except: don't remove my name from the source or
|
||||
.\" * documentation (don't take credit for my work), mark your changes (don't
|
||||
.\" * get me blamed for your possible bugs), don't alter or remove this
|
||||
.\" * notice. May be sold if buildable source is provided to buyer. No
|
||||
.\" * warrantee of any kind, express or implied, is included with this
|
||||
.\" * software; use at your own risk, responsibility for damages (if any) to
|
||||
.\" * anyone resulting from the use of this software rests entirely with the
|
||||
.\" * user.
|
||||
.\" *
|
||||
.\" * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
|
||||
.\" * I'll try to keep a version up to date. I can be reached as follows:
|
||||
.\" * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
|
||||
.\" */
|
||||
.\"
|
||||
.\" $Id: crontab.1,v 2.4 1993/12/31 10:47:33 vixie Exp $
|
||||
.\"
|
||||
.TH CRONTAB 1 "29 December 1993"
|
||||
.UC 4
|
||||
.SH NAME
|
||||
crontab \- maintain crontab files for individual users (V3)
|
||||
.SH SYNOPSIS
|
||||
crontab [ -u user ] file
|
||||
.br
|
||||
crontab [ -u user ] { -l | -r | -e }
|
||||
.SH DESCRIPTION
|
||||
.I Crontab
|
||||
is the program used to install, deinstall or list the tables
|
||||
used to drive the
|
||||
.IR cron (8)
|
||||
daemon in Vixie Cron. Each user can have their own crontab, and though
|
||||
these are files in /var, they are not intended to be edited directly.
|
||||
.PP
|
||||
If the
|
||||
.I allow
|
||||
file exists, then you must be listed therein in order to be allowed to use
|
||||
this command. If the
|
||||
.I allow
|
||||
file does not exist but the
|
||||
.I deny
|
||||
file does exist, then you must \fBnot\fR be listed in the
|
||||
.I deny
|
||||
file in order to use this command. If neither of these files exists, then
|
||||
depending on site-dependent configuration parameters, only the super user
|
||||
will be allowed to use this command, or all users will be able to use this
|
||||
command.
|
||||
.PP
|
||||
If the
|
||||
.I -u
|
||||
option is given, it specifies the name of the user whose crontab is to be
|
||||
tweaked. If this option is not given,
|
||||
.I crontab
|
||||
examines "your" crontab, i.e., the crontab of the person executing the
|
||||
command. Note that
|
||||
.IR su (8)
|
||||
can confuse
|
||||
.I crontab
|
||||
and that if you are running inside of
|
||||
.IR su (8)
|
||||
you should always use the
|
||||
.I -u
|
||||
option for safety's sake.
|
||||
.PP
|
||||
The first form of this command is used to install a new crontab from some
|
||||
named file or standard input if the pseudo-filename ``-'' is given.
|
||||
.PP
|
||||
The
|
||||
.I -l
|
||||
option causes the current crontab to be displayed on standard output.
|
||||
.PP
|
||||
The
|
||||
.I -r
|
||||
option causes the current crontab to be removed.
|
||||
.PP
|
||||
The
|
||||
.I -e
|
||||
option is used to edit the current crontab using the editor specified by
|
||||
the \s-1VISUAL\s+1 or \s-1EDITOR\s+1 environment variables. After you exit
|
||||
from the editor, the modified crontab will be installed automatically.
|
||||
.SH "SEE ALSO"
|
||||
crontab(5), cron(8)
|
||||
.SH FILES
|
||||
.nf
|
||||
/var/cron/allow
|
||||
/var/cron/deny
|
||||
.fi
|
||||
.SH STANDARDS
|
||||
The
|
||||
.I crontab
|
||||
command conforms to IEEE Std1003.2-1992 (``POSIX''). This new command syntax
|
||||
differs from previous versions of Vixie Cron, as well as from the classic
|
||||
SVR3 syntax.
|
||||
.SH DIAGNOSTICS
|
||||
A fairly informative usage message appears if you run it with a bad command
|
||||
line.
|
||||
.SH AUTHOR
|
||||
.nf
|
||||
Paul Vixie <paul@vix.com>
|
||||
188
src/cmd/cron/crontab.5
Executable file
188
src/cmd/cron/crontab.5
Executable file
@@ -0,0 +1,188 @@
|
||||
.\"/* Copyright 1988,1990,1993,1994 by Paul Vixie
|
||||
.\" * All rights reserved
|
||||
.\" *
|
||||
.\" * Distribute freely, except: don't remove my name from the source or
|
||||
.\" * documentation (don't take credit for my work), mark your changes (don't
|
||||
.\" * get me blamed for your possible bugs), don't alter or remove this
|
||||
.\" * notice. May be sold if buildable source is provided to buyer. No
|
||||
.\" * warrantee of any kind, express or implied, is included with this
|
||||
.\" * software; use at your own risk, responsibility for damages (if any) to
|
||||
.\" * anyone resulting from the use of this software rests entirely with the
|
||||
.\" * user.
|
||||
.\" *
|
||||
.\" * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
|
||||
.\" * I'll try to keep a version up to date. I can be reached as follows:
|
||||
.\" * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
|
||||
.\" */
|
||||
.\"
|
||||
.\" $Id: crontab.5,v 2.4 1994/01/15 20:43:43 vixie Exp $
|
||||
.\"
|
||||
.TH CRONTAB 5 "24 January 1994"
|
||||
.UC 4
|
||||
.SH NAME
|
||||
crontab \- tables for driving cron
|
||||
.SH DESCRIPTION
|
||||
A
|
||||
.I crontab
|
||||
file contains instructions to the
|
||||
.IR cron (8)
|
||||
daemon of the general form: ``run this command at this time on this date''.
|
||||
Each user has their own crontab, and commands in any given crontab will be
|
||||
executed as the user who owns the crontab. Uucp and News will usually have
|
||||
their own crontabs, eliminating the need for explicitly running
|
||||
.IR su (1)
|
||||
as part of a cron command.
|
||||
.PP
|
||||
Blank lines and leading spaces and tabs are ignored. Lines whose first
|
||||
non-space character is a pound-sign (#) are comments, and are ignored.
|
||||
Note that comments are not allowed on the same line as cron commands, since
|
||||
they will be taken to be part of the command. Similarly, comments are not
|
||||
allowed on the same line as environment variable settings.
|
||||
.PP
|
||||
An active line in a crontab will be either an environment setting or a cron
|
||||
command. An environment setting is of the form,
|
||||
.PP
|
||||
name = value
|
||||
.PP
|
||||
where the spaces around the equal-sign (=) are optional, and any subsequent
|
||||
non-leading spaces in
|
||||
.I value
|
||||
will be part of the value assigned to
|
||||
.IR name .
|
||||
The
|
||||
.I value
|
||||
string may be placed in quotes (single or double, but matching) to preserve
|
||||
leading or trailing blanks.
|
||||
.PP
|
||||
Several environment variables are set up
|
||||
automatically by the
|
||||
.IR cron (8)
|
||||
daemon.
|
||||
SHELL is set to /bin/sh, and LOGNAME and HOME are set from the /etc/passwd
|
||||
line of the crontab's owner.
|
||||
HOME and SHELL may be overridden by settings in the crontab; LOGNAME may not.
|
||||
.PP
|
||||
(Another note: the LOGNAME variable is sometimes called USER on BSD systems...
|
||||
on these systems, USER will be set also.)
|
||||
.PP
|
||||
In addition to LOGNAME, HOME, and SHELL,
|
||||
.IR cron (8)
|
||||
will look at MAILTO if it has any reason to send mail as a result of running
|
||||
commands in ``this'' crontab. If MAILTO is defined (and non-empty), mail is
|
||||
sent to the user so named. If MAILTO is defined but empty (MAILTO=""), no
|
||||
mail will be sent. Otherwise mail is sent to the owner of the crontab. This
|
||||
option is useful if you decide on /bin/mail instead of /usr/lib/sendmail as
|
||||
your mailer when you install cron -- /bin/mail doesn't do aliasing, and UUCP
|
||||
usually doesn't read its mail.
|
||||
.PP
|
||||
The format of a cron command is very much the V7 standard, with a number of
|
||||
upward-compatible extensions. Each line has five time and date fields,
|
||||
followed by a user name if this is the system crontab file,
|
||||
followed by a command. Commands are executed by
|
||||
.IR cron (8)
|
||||
when the minute, hour, and month of year fields match the current time,
|
||||
.I and
|
||||
when at least one of the two day fields (day of month, or day of week)
|
||||
match the current time (see ``Note'' below).
|
||||
.IR cron (8)
|
||||
examines cron entries once every minute.
|
||||
The time and date fields are:
|
||||
.IP
|
||||
.ta 1.5i
|
||||
field allowed values
|
||||
.br
|
||||
----- --------------
|
||||
.br
|
||||
minute 0-59
|
||||
.br
|
||||
hour 0-23
|
||||
.br
|
||||
day of month 0-31
|
||||
.br
|
||||
month 0-12 (or names, see below)
|
||||
.br
|
||||
day of week 0-7 (0 or 7 is Sun, or use names)
|
||||
.br
|
||||
.PP
|
||||
A field may be an asterisk (*), which always stands for ``first\-last''.
|
||||
.PP
|
||||
Ranges of numbers are allowed. Ranges are two numbers separated
|
||||
with a hyphen. The specified range is inclusive. For example,
|
||||
8-11 for an ``hours'' entry specifies execution at hours 8, 9, 10
|
||||
and 11.
|
||||
.PP
|
||||
Lists are allowed. A list is a set of numbers (or ranges)
|
||||
separated by commas. Examples: ``1,2,5,9'', ``0-4,8-12''.
|
||||
.PP
|
||||
Step values can be used in conjunction with ranges. Following
|
||||
a range with ``/<number>'' specifies skips of the number's value
|
||||
through the range. For example, ``0-23/2'' can be used in the hours
|
||||
field to specify command execution every other hour (the alternative
|
||||
in the V7 standard is ``0,2,4,6,8,10,12,14,16,18,20,22''). Steps are
|
||||
also permitted after an asterisk, so if you want to say ``every two
|
||||
hours'', just use ``*/2''.
|
||||
.PP
|
||||
Names can also be used for the ``month'' and ``day of week''
|
||||
fields. Use the first three letters of the particular
|
||||
day or month (case doesn't matter). Ranges or
|
||||
lists of names are not allowed.
|
||||
.PP
|
||||
The ``sixth'' field (the rest of the line) specifies the command to be
|
||||
run.
|
||||
The entire command portion of the line, up to a newline or %
|
||||
character, will be executed by /bin/sh or by the shell
|
||||
specified in the SHELL variable of the cronfile.
|
||||
Percent-signs (%) in the command, unless escaped with backslash
|
||||
(\\), will be changed into newline characters, and all data
|
||||
after the first % will be sent to the command as standard
|
||||
input.
|
||||
.PP
|
||||
Note: The day of a command's execution can be specified by two
|
||||
fields \(em day of month, and day of week. If both fields are
|
||||
restricted (ie, aren't *), the command will be run when
|
||||
.I either
|
||||
field matches the current time. For example,
|
||||
.br
|
||||
``30 4 1,15 * 5''
|
||||
would cause a command to be run at 4:30 am on the 1st and 15th of each
|
||||
month, plus every Friday.
|
||||
.SH EXAMPLE CRON FILE
|
||||
.nf
|
||||
|
||||
# use /bin/sh to run commands, no matter what /etc/passwd says
|
||||
SHELL=/bin/sh
|
||||
# mail any output to `paul', no matter whose crontab this is
|
||||
MAILTO=paul
|
||||
#
|
||||
# run five minutes after midnight, every day
|
||||
5 0 * * * $HOME/bin/daily.job >> $HOME/tmp/out 2>&1
|
||||
# run at 2:15pm on the first of every month -- output mailed to paul
|
||||
15 14 1 * * $HOME/bin/monthly
|
||||
# run at 10 pm on weekdays, annoy Joe
|
||||
0 22 * * 1-5 mail -s "It's 10pm" joe%Joe,%%Where are your kids?%
|
||||
23 0-23/2 * * * echo "run 23 minutes after midn, 2am, 4am ..., everyday"
|
||||
5 4 * * sun echo "run at 5 after 4 every sunday"
|
||||
.fi
|
||||
.SH SEE ALSO
|
||||
cron(8), crontab(1)
|
||||
.SH EXTENSIONS
|
||||
When specifying day of week, both day 0 and day 7 will be considered Sunday.
|
||||
BSD and ATT seem to disagree about this.
|
||||
.PP
|
||||
Lists and ranges are allowed to co-exist in the same field. "1-3,7-9" would
|
||||
be rejected by ATT or BSD cron -- they want to see "1-3" or "7,8,9" ONLY.
|
||||
.PP
|
||||
Ranges can include "steps", so "1-9/2" is the same as "1,3,5,7,9".
|
||||
.PP
|
||||
Names of months or days of the week can be specified by name.
|
||||
.PP
|
||||
Environment variables can be set in the crontab. In BSD or ATT, the
|
||||
environment handed to child processes is basically the one from /etc/rc.
|
||||
.PP
|
||||
Command output is mailed to the crontab owner (BSD can't do this), can be
|
||||
mailed to a person other than the crontab owner (SysV can't do this), or the
|
||||
feature can be turned off and no mail will be sent at all (SysV can't do this
|
||||
either).
|
||||
.SH AUTHOR
|
||||
.nf
|
||||
Paul Vixie <paul@vix.com>
|
||||
586
src/cmd/cron/crontab.c
Executable file
586
src/cmd/cron/crontab.c
Executable file
@@ -0,0 +1,586 @@
|
||||
/* Copyright 1988,1990,1993,1994 by Paul Vixie
|
||||
* All rights reserved
|
||||
*
|
||||
* Distribute freely, except: don't remove my name from the source or
|
||||
* documentation (don't take credit for my work), mark your changes (don't
|
||||
* get me blamed for your possible bugs), don't alter or remove this
|
||||
* notice. May be sold if buildable source is provided to buyer. No
|
||||
* warrantee of any kind, express or implied, is included with this
|
||||
* software; use at your own risk, responsibility for damages (if any) to
|
||||
* anyone resulting from the use of this software rests entirely with the
|
||||
* user.
|
||||
*
|
||||
* Send bug reports, bug fixes, enhancements, requests, flames, etc., and
|
||||
* I'll try to keep a version up to date. I can be reached as follows:
|
||||
* Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
|
||||
*/
|
||||
|
||||
static char sccsid[] = "@(#) crontab.c 2.13.1 (2.11BSD) 1999/8/9";
|
||||
|
||||
/* crontab - install and manage per-user crontab files
|
||||
* vix 02may87 [RCS has the rest of the log]
|
||||
* vix 26jan87 [original]
|
||||
*/
|
||||
|
||||
#define MAIN_PROGRAM
|
||||
|
||||
#include "cron.h"
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#define NHEADER_LINES 3
|
||||
|
||||
enum opt_t { opt_unknown, opt_list, opt_delete, opt_edit, opt_replace };
|
||||
|
||||
#if DEBUGGING
|
||||
static char *Options[] = { "???", "list", "delete", "edit", "replace" };
|
||||
#endif
|
||||
|
||||
|
||||
static PID_T Pid;
|
||||
static char User[MAX_UNAME], RealUser[MAX_UNAME];
|
||||
static char Filename[MAX_FNAME];
|
||||
static FILE *NewCrontab;
|
||||
static int CheckErrorCount;
|
||||
static enum opt_t Option;
|
||||
static struct passwd *pw;
|
||||
static void list_cmd __P((void)),
|
||||
delete_cmd __P((void)),
|
||||
edit_cmd __P((void)),
|
||||
poke_daemon __P((void)),
|
||||
check_error __P((char *)),
|
||||
parse_args __P((int c, char *v[]));
|
||||
static int replace_cmd __P((void));
|
||||
|
||||
|
||||
static void
|
||||
usage(msg)
|
||||
char *msg;
|
||||
{
|
||||
fprintf(stderr, "%s: usage error: %s\n", ProgramName, msg);
|
||||
fprintf(stderr, "usage:\t%s [-u user] file\n", ProgramName);
|
||||
fprintf(stderr, "\t%s [-u user] { -e | -l | -r }\n", ProgramName);
|
||||
fprintf(stderr, "\t\t(default operation is replace, per 1003.2)\n");
|
||||
fprintf(stderr, "\t-e\t(edit user's crontab)\n");
|
||||
fprintf(stderr, "\t-l\t(list user's crontab)\n");
|
||||
fprintf(stderr, "\t-r\t(delete user's crontab)\n");
|
||||
exit(ERROR_EXIT);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(argc, argv)
|
||||
int argc;
|
||||
char *argv[];
|
||||
{
|
||||
int exitstatus;
|
||||
|
||||
Pid = getpid();
|
||||
ProgramName = argv[0];
|
||||
|
||||
setlinebuf(stderr);
|
||||
|
||||
parse_args(argc, argv); /* sets many globals, opens a file */
|
||||
set_cron_uid();
|
||||
set_cron_cwd();
|
||||
if (!allowed(User)) {
|
||||
fprintf(stderr,
|
||||
"You (%s) are not allowed to use this program (%s)\n",
|
||||
User, ProgramName);
|
||||
fprintf(stderr, "See crontab(1) for more information\n");
|
||||
log_it(RealUser, Pid, "AUTH", "crontab command not allowed");
|
||||
exit(ERROR_EXIT);
|
||||
}
|
||||
exitstatus = OK_EXIT;
|
||||
switch (Option) {
|
||||
case opt_list: list_cmd();
|
||||
break;
|
||||
case opt_delete: delete_cmd();
|
||||
break;
|
||||
case opt_edit: edit_cmd();
|
||||
break;
|
||||
case opt_replace: if (replace_cmd() < 0)
|
||||
exitstatus = ERROR_EXIT;
|
||||
break;
|
||||
}
|
||||
exit(0);
|
||||
/*NOTREACHED*/
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
parse_args(argc, argv)
|
||||
int argc;
|
||||
char *argv[];
|
||||
{
|
||||
int argch;
|
||||
|
||||
if (!(pw = getpwuid(getuid()))) {
|
||||
fprintf(stderr, "%s: your UID isn't in the passwd file.\n",
|
||||
ProgramName);
|
||||
fprintf(stderr, "bailing out.\n");
|
||||
exit(ERROR_EXIT);
|
||||
}
|
||||
strcpy(User, pw->pw_name);
|
||||
strcpy(RealUser, User);
|
||||
Filename[0] = '\0';
|
||||
Option = opt_unknown;
|
||||
while (EOF != (argch = getopt(argc, argv, "u:lerx:"))) {
|
||||
switch (argch) {
|
||||
case 'x':
|
||||
if (!set_debug_flags(optarg))
|
||||
usage("bad debug option");
|
||||
break;
|
||||
case 'u':
|
||||
if (getuid() != ROOT_UID)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"must be privileged to use -u\n");
|
||||
exit(ERROR_EXIT);
|
||||
}
|
||||
if (!(pw = getpwnam(optarg)))
|
||||
{
|
||||
fprintf(stderr, "%s: user `%s' unknown\n",
|
||||
ProgramName, optarg);
|
||||
exit(ERROR_EXIT);
|
||||
}
|
||||
(void) strcpy(User, optarg);
|
||||
break;
|
||||
case 'l':
|
||||
if (Option != opt_unknown)
|
||||
usage("only one operation permitted");
|
||||
Option = opt_list;
|
||||
break;
|
||||
case 'r':
|
||||
if (Option != opt_unknown)
|
||||
usage("only one operation permitted");
|
||||
Option = opt_delete;
|
||||
break;
|
||||
case 'e':
|
||||
if (Option != opt_unknown)
|
||||
usage("only one operation permitted");
|
||||
Option = opt_edit;
|
||||
break;
|
||||
default:
|
||||
usage("unrecognized option");
|
||||
}
|
||||
}
|
||||
|
||||
endpwent();
|
||||
|
||||
if (Option != opt_unknown) {
|
||||
if (argv[optind] != NULL) {
|
||||
usage("no arguments permitted after this option");
|
||||
}
|
||||
} else {
|
||||
if (argv[optind] != NULL) {
|
||||
Option = opt_replace;
|
||||
(void) strcpy (Filename, argv[optind]);
|
||||
} else {
|
||||
usage("file name must be specified for replace");
|
||||
}
|
||||
}
|
||||
|
||||
if (Option == opt_replace) {
|
||||
/* we have to open the file here because we're going to
|
||||
* chdir(2) into /var/cron before we get around to
|
||||
* reading the file.
|
||||
*/
|
||||
if (!strcmp(Filename, "-")) {
|
||||
NewCrontab = stdin;
|
||||
} else {
|
||||
/* relinquish the setuid status of the binary during
|
||||
* the open, lest nonroot users read files they should
|
||||
* not be able to read. we can't use access() here
|
||||
* since there's a race condition. thanks go out to
|
||||
* Arnt Gulbrandsen <agulbra@pvv.unit.no> for spotting
|
||||
* the race.
|
||||
*/
|
||||
|
||||
if (swap_uids() < OK) {
|
||||
perror("swapping uids");
|
||||
exit(ERROR_EXIT);
|
||||
}
|
||||
if (!(NewCrontab = fopen(Filename, "r"))) {
|
||||
perror(Filename);
|
||||
exit(ERROR_EXIT);
|
||||
}
|
||||
if (swap_uids_back() < OK) {
|
||||
perror("swapping uids back");
|
||||
exit(ERROR_EXIT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Debug(DMISC, ("user=%s, file=%s, option=%s\n",
|
||||
User, Filename, Options[(int)Option]))
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
list_cmd() {
|
||||
char n[MAX_FNAME];
|
||||
register FILE *f;
|
||||
int ch;
|
||||
|
||||
log_it(RealUser, Pid, "LIST", User);
|
||||
(void) sprintf(n, CRON_TAB(User));
|
||||
if (!(f = fopen(n, "r"))) {
|
||||
if (errno == ENOENT)
|
||||
fprintf(stderr, "no crontab for %s\n", User);
|
||||
else
|
||||
perror(n);
|
||||
exit(ERROR_EXIT);
|
||||
}
|
||||
|
||||
/* file is open. copy to stdout, close.
|
||||
*/
|
||||
Set_LineNum(1)
|
||||
while (EOF != (ch = get_char(f)))
|
||||
putchar(ch);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
delete_cmd() {
|
||||
char n[MAX_FNAME];
|
||||
|
||||
log_it(RealUser, Pid, "DELETE", User);
|
||||
(void) sprintf(n, CRON_TAB(User));
|
||||
if (unlink(n)) {
|
||||
if (errno == ENOENT)
|
||||
fprintf(stderr, "no crontab for %s\n", User);
|
||||
else
|
||||
perror(n);
|
||||
exit(ERROR_EXIT);
|
||||
}
|
||||
poke_daemon();
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
check_error(msg)
|
||||
char *msg;
|
||||
{
|
||||
CheckErrorCount++;
|
||||
fprintf(stderr, "\"%s\":%d: %s\n", Filename, LineNumber-1, msg);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
edit_cmd() {
|
||||
char n[MAX_FNAME], q[MAX_TEMPSTR], *editor;
|
||||
register FILE *f;
|
||||
register int ch;
|
||||
int t, x;
|
||||
struct stat statbuf;
|
||||
time_t mtime;
|
||||
WAIT_T waiter;
|
||||
PID_T pid, xpid;
|
||||
|
||||
log_it(RealUser, Pid, "BEGIN EDIT", User);
|
||||
(void) sprintf(n, CRON_TAB(User));
|
||||
if (!(f = fopen(n, "r"))) {
|
||||
if (errno != ENOENT) {
|
||||
perror(n);
|
||||
exit(ERROR_EXIT);
|
||||
}
|
||||
fprintf(stderr, "no crontab for %s - using an empty one\n",
|
||||
User);
|
||||
if (!(f = fopen("/dev/null", "r"))) {
|
||||
perror("/dev/null");
|
||||
exit(ERROR_EXIT);
|
||||
}
|
||||
}
|
||||
|
||||
(void) sprintf(Filename, "/tmp/crontab.%d", Pid);
|
||||
if (-1 == (t = open(Filename, O_CREAT|O_EXCL|O_RDWR, 0600))) {
|
||||
perror(Filename);
|
||||
goto fatal;
|
||||
}
|
||||
if (fchown(t, getuid(), getgid()) < 0) {
|
||||
perror("fchown");
|
||||
goto fatal;
|
||||
}
|
||||
if (!(NewCrontab = fdopen(t, "r+"))) {
|
||||
perror("fdopen");
|
||||
goto fatal;
|
||||
}
|
||||
|
||||
Set_LineNum(1)
|
||||
|
||||
/* ignore the top few comments since we probably put them there.
|
||||
*/
|
||||
for (x = 0; x < NHEADER_LINES; x++) {
|
||||
ch = get_char(f);
|
||||
if (EOF == ch)
|
||||
break;
|
||||
if ('#' != ch) {
|
||||
putc(ch, NewCrontab);
|
||||
break;
|
||||
}
|
||||
while (EOF != (ch = get_char(f)))
|
||||
if (ch == '\n')
|
||||
break;
|
||||
if (EOF == ch)
|
||||
break;
|
||||
}
|
||||
|
||||
/* copy the rest of the crontab (if any) to the temp file.
|
||||
*/
|
||||
if (EOF != ch)
|
||||
while (EOF != (ch = get_char(f)))
|
||||
putc(ch, NewCrontab);
|
||||
fclose(f);
|
||||
if (fflush(NewCrontab) < OK) {
|
||||
perror(Filename);
|
||||
exit(ERROR_EXIT);
|
||||
}
|
||||
again:
|
||||
rewind(NewCrontab);
|
||||
if (ferror(NewCrontab)) {
|
||||
fprintf(stderr, "%s: error while writing new crontab to %s\n",
|
||||
ProgramName, Filename);
|
||||
fatal: unlink(Filename);
|
||||
exit(ERROR_EXIT);
|
||||
}
|
||||
if (fstat(t, &statbuf) < 0) {
|
||||
perror("fstat");
|
||||
goto fatal;
|
||||
}
|
||||
mtime = statbuf.st_mtime;
|
||||
|
||||
if ((!(editor = getenv("VISUAL")))
|
||||
&& (!(editor = getenv("EDITOR")))
|
||||
) {
|
||||
editor = EDITOR;
|
||||
}
|
||||
|
||||
/* we still have the file open. editors will generally rewrite the
|
||||
* original file rather than renaming/unlinking it and starting a
|
||||
* new one; even backup files are supposed to be made by copying
|
||||
* rather than by renaming. if some editor does not support this,
|
||||
* then don't use it. the security problems are more severe if we
|
||||
* close and reopen the file around the edit.
|
||||
*/
|
||||
|
||||
switch (pid = fork()) {
|
||||
case -1:
|
||||
perror("fork");
|
||||
goto fatal;
|
||||
case 0:
|
||||
/* child */
|
||||
if (setuid(getuid()) < 0) {
|
||||
perror("setuid(getuid())");
|
||||
exit(ERROR_EXIT);
|
||||
}
|
||||
if (chdir("/tmp") < 0) {
|
||||
perror("chdir(/tmp)");
|
||||
exit(ERROR_EXIT);
|
||||
}
|
||||
if (strlen(editor) + strlen(Filename) + 2 >= MAX_TEMPSTR) {
|
||||
fprintf(stderr, "%s: editor or filename too long\n",
|
||||
ProgramName);
|
||||
exit(ERROR_EXIT);
|
||||
}
|
||||
sprintf(q, "%s %s", editor, Filename);
|
||||
execlp(_PATH_BSHELL, _PATH_BSHELL, "-c", q, NULL);
|
||||
perror(editor);
|
||||
exit(ERROR_EXIT);
|
||||
/*NOTREACHED*/
|
||||
default:
|
||||
/* parent */
|
||||
break;
|
||||
}
|
||||
|
||||
/* parent */
|
||||
xpid = wait(&waiter);
|
||||
if (xpid != pid) {
|
||||
fprintf(stderr, "%s: wrong PID (%d != %d) from \"%s\"\n",
|
||||
ProgramName, xpid, pid, editor);
|
||||
goto fatal;
|
||||
}
|
||||
if (WIFEXITED(waiter) && WEXITSTATUS(waiter)) {
|
||||
fprintf(stderr, "%s: \"%s\" exited with status %d\n",
|
||||
ProgramName, editor, WEXITSTATUS(waiter));
|
||||
goto fatal;
|
||||
}
|
||||
if (WIFSIGNALED(waiter)) {
|
||||
fprintf(stderr,
|
||||
"%s: \"%s\" killed; signal %d (%score dumped)\n",
|
||||
ProgramName, editor, WTERMSIG(waiter),
|
||||
WCOREDUMP(waiter) ?"" :"no ");
|
||||
goto fatal;
|
||||
}
|
||||
if (fstat(t, &statbuf) < 0) {
|
||||
perror("fstat");
|
||||
goto fatal;
|
||||
}
|
||||
if (mtime == statbuf.st_mtime) {
|
||||
fprintf(stderr, "%s: no changes made to crontab\n",
|
||||
ProgramName);
|
||||
goto remove;
|
||||
}
|
||||
fprintf(stderr, "%s: installing new crontab\n", ProgramName);
|
||||
switch (replace_cmd()) {
|
||||
case 0:
|
||||
break;
|
||||
case -1:
|
||||
for (;;) {
|
||||
printf("Do you want to retry the same edit? ");
|
||||
fflush(stdout);
|
||||
q[0] = '\0';
|
||||
(void) fgets(q, sizeof q, stdin);
|
||||
switch (islower(q[0]) ? q[0] : tolower(q[0])) {
|
||||
case 'y':
|
||||
goto again;
|
||||
case 'n':
|
||||
goto abandon;
|
||||
default:
|
||||
fprintf(stderr, "Enter Y or N\n");
|
||||
}
|
||||
}
|
||||
/*NOTREACHED*/
|
||||
case -2:
|
||||
abandon:
|
||||
fprintf(stderr, "%s: edits left in %s\n",
|
||||
ProgramName, Filename);
|
||||
goto done;
|
||||
default:
|
||||
fprintf(stderr, "%s: panic: bad switch() in replace_cmd()\n");
|
||||
goto fatal;
|
||||
}
|
||||
remove:
|
||||
unlink(Filename);
|
||||
done:
|
||||
log_it(RealUser, Pid, "END EDIT", User);
|
||||
}
|
||||
|
||||
|
||||
/* returns 0 on success
|
||||
* -1 on syntax error
|
||||
* -2 on install error
|
||||
*/
|
||||
static int
|
||||
replace_cmd() {
|
||||
char n[MAX_FNAME], envstr[MAX_ENVSTR], tn[MAX_FNAME];
|
||||
register FILE *tmp;
|
||||
register int ch;
|
||||
int eof;
|
||||
entry *e;
|
||||
time_t now = time(NULL);
|
||||
char **envp = env_init();
|
||||
|
||||
(void) sprintf(n, "tmp.%d", Pid);
|
||||
(void) sprintf(tn, CRON_TAB(n));
|
||||
if (!(tmp = fopen(tn, "w+"))) {
|
||||
perror(tn);
|
||||
return (-2);
|
||||
}
|
||||
|
||||
/* write a signature at the top of the file.
|
||||
*
|
||||
* VERY IMPORTANT: make sure NHEADER_LINES agrees with this code.
|
||||
*/
|
||||
fprintf(tmp, "# DO NOT EDIT THIS FILE - edit the master and reinstall.\n");
|
||||
fprintf(tmp, "# (%s installed on %-24.24s)\n", Filename, ctime(&now));
|
||||
fprintf(tmp, "# (Cron version -- %s)\n", sccsid);
|
||||
|
||||
/* copy the crontab to the tmp
|
||||
*/
|
||||
rewind(NewCrontab);
|
||||
Set_LineNum(1)
|
||||
while (EOF != (ch = get_char(NewCrontab)))
|
||||
putc(ch, tmp);
|
||||
ftruncate(fileno(tmp), ftell(tmp));
|
||||
fflush(tmp); rewind(tmp);
|
||||
|
||||
if (ferror(tmp)) {
|
||||
fprintf(stderr, "%s: error while writing new crontab to %s\n",
|
||||
ProgramName, tn);
|
||||
fclose(tmp); unlink(tn);
|
||||
return (-2);
|
||||
}
|
||||
|
||||
/* check the syntax of the file being installed.
|
||||
*/
|
||||
|
||||
/* BUG: was reporting errors after the EOF if there were any errors
|
||||
* in the file proper -- kludged it by stopping after first error.
|
||||
* vix 31mar87
|
||||
*/
|
||||
Set_LineNum(1 - NHEADER_LINES)
|
||||
CheckErrorCount = 0; eof = FALSE;
|
||||
while (!CheckErrorCount && !eof) {
|
||||
switch (load_env(envstr, tmp)) {
|
||||
case ERR:
|
||||
eof = TRUE;
|
||||
break;
|
||||
case FALSE:
|
||||
e = load_entry(tmp, check_error, pw, envp);
|
||||
if (e)
|
||||
free(e);
|
||||
break;
|
||||
case TRUE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (CheckErrorCount != 0) {
|
||||
fprintf(stderr, "errors in crontab file, can't install.\n");
|
||||
fclose(tmp); unlink(tn);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (fchown(fileno(tmp), ROOT_UID, -1) < OK)
|
||||
{
|
||||
perror("chown");
|
||||
fclose(tmp); unlink(tn);
|
||||
return (-2);
|
||||
}
|
||||
|
||||
if (fchmod(fileno(tmp), 0600) < OK)
|
||||
{
|
||||
perror("chown");
|
||||
fclose(tmp); unlink(tn);
|
||||
return (-2);
|
||||
}
|
||||
|
||||
if (fclose(tmp) == EOF) {
|
||||
perror("fclose");
|
||||
unlink(tn);
|
||||
return (-2);
|
||||
}
|
||||
|
||||
(void) sprintf(n, CRON_TAB(User));
|
||||
if (rename(tn, n)) {
|
||||
fprintf(stderr, "%s: error renaming %s to %s\n",
|
||||
ProgramName, tn, n);
|
||||
perror("rename");
|
||||
unlink(tn);
|
||||
return (-2);
|
||||
}
|
||||
log_it(RealUser, Pid, "REPLACE", User);
|
||||
|
||||
poke_daemon();
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
poke_daemon() {
|
||||
struct timeval tvs[2];
|
||||
|
||||
(void) gettimeofday(&tvs[0], NULL);
|
||||
tvs[1] = tvs[0];
|
||||
if (utimes(SPOOL_DIR, tvs) < OK) {
|
||||
fprintf(stderr, "crontab: can't update mtime on spooldir\n");
|
||||
perror(SPOOL_DIR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
261
src/cmd/cron/database.c
Executable file
261
src/cmd/cron/database.c
Executable file
@@ -0,0 +1,261 @@
|
||||
/* Copyright 1988,1990,1993,1994 by Paul Vixie
|
||||
* All rights reserved
|
||||
*
|
||||
* Distribute freely, except: don't remove my name from the source or
|
||||
* documentation (don't take credit for my work), mark your changes (don't
|
||||
* get me blamed for your possible bugs), don't alter or remove this
|
||||
* notice. May be sold if buildable source is provided to buyer. No
|
||||
* warrantee of any kind, express or implied, is included with this
|
||||
* software; use at your own risk, responsibility for damages (if any) to
|
||||
* anyone resulting from the use of this software rests entirely with the
|
||||
* user.
|
||||
*
|
||||
* Send bug reports, bug fixes, enhancements, requests, flames, etc., and
|
||||
* I'll try to keep a version up to date. I can be reached as follows:
|
||||
* Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
|
||||
*/
|
||||
|
||||
#if !defined(lint) && !defined(LINT)
|
||||
static char rcsid[] = "$Id: database.c,v 2.8 1994/01/15 20:43:43 vixie Exp $";
|
||||
#endif
|
||||
|
||||
/* vix 26jan87 [RCS has the log]
|
||||
*/
|
||||
|
||||
|
||||
#include "cron.h"
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/file.h>
|
||||
|
||||
|
||||
#define TMAX(a,b) ((a)>(b)?(a):(b))
|
||||
|
||||
|
||||
static void process_crontab __P((char *, char *, char *,
|
||||
struct stat *,
|
||||
cron_db *, cron_db *));
|
||||
|
||||
|
||||
void
|
||||
load_database(old_db)
|
||||
cron_db *old_db;
|
||||
{
|
||||
DIR *dir;
|
||||
struct stat statbuf;
|
||||
struct stat syscron_stat;
|
||||
register DIR_T *dp;
|
||||
cron_db new_db;
|
||||
user *u, *nu;
|
||||
|
||||
Debug(DLOAD, ("[%d] load_database()\n", getpid()))
|
||||
|
||||
/* before we start loading any data, do a stat on SPOOL_DIR
|
||||
* so that if anything changes as of this moment (i.e., before we've
|
||||
* cached any of the database), we'll see the changes next time.
|
||||
*/
|
||||
if (stat(SPOOL_DIR, &statbuf) < OK) {
|
||||
log_it("CRON", getpid(), "STAT FAILED", SPOOL_DIR);
|
||||
(void) exit(ERROR_EXIT);
|
||||
}
|
||||
|
||||
/* track system crontab file
|
||||
*/
|
||||
if (stat(SYSCRONTAB, &syscron_stat) < OK)
|
||||
syscron_stat.st_mtime = 0;
|
||||
|
||||
/* if spooldir's mtime has not changed, we don't need to fiddle with
|
||||
* the database.
|
||||
*
|
||||
* Note that old_db->mtime is initialized to 0 in main(), and
|
||||
* so is guaranteed to be different than the stat() mtime the first
|
||||
* time this function is called.
|
||||
*/
|
||||
if (old_db->mtime == TMAX(statbuf.st_mtime, syscron_stat.st_mtime)) {
|
||||
Debug(DLOAD, ("[%d] spool dir mtime unch, no load needed.\n",
|
||||
getpid()))
|
||||
return;
|
||||
}
|
||||
|
||||
/* something's different. make a new database, moving unchanged
|
||||
* elements from the old database, reloading elements that have
|
||||
* actually changed. Whatever is left in the old database when
|
||||
* we're done is chaff -- crontabs that disappeared.
|
||||
*/
|
||||
new_db.mtime = TMAX(statbuf.st_mtime, syscron_stat.st_mtime);
|
||||
new_db.head = new_db.tail = NULL;
|
||||
|
||||
if (syscron_stat.st_mtime) {
|
||||
process_crontab("root", "*system*",
|
||||
SYSCRONTAB, &syscron_stat,
|
||||
&new_db, old_db);
|
||||
}
|
||||
|
||||
/* we used to keep this dir open all the time, for the sake of
|
||||
* efficiency. however, we need to close it in every fork, and
|
||||
* we fork a lot more often than the mtime of the dir changes.
|
||||
*/
|
||||
if (!(dir = opendir(SPOOL_DIR))) {
|
||||
log_it("CRON", getpid(), "OPENDIR FAILED", SPOOL_DIR);
|
||||
(void) exit(ERROR_EXIT);
|
||||
}
|
||||
|
||||
while (NULL != (dp = readdir(dir))) {
|
||||
char fname[MAXNAMLEN+1],
|
||||
tabname[MAXNAMLEN+1];
|
||||
|
||||
/* avoid file names beginning with ".". this is good
|
||||
* because we would otherwise waste two guaranteed calls
|
||||
* to getpwnam() for . and .., and also because user names
|
||||
* starting with a period are just too nasty to consider.
|
||||
*/
|
||||
if (dp->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
(void) strcpy(fname, dp->d_name);
|
||||
sprintf(tabname, CRON_TAB(fname));
|
||||
|
||||
process_crontab(fname, fname, tabname,
|
||||
&statbuf, &new_db, old_db);
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
/* if we don't do this, then when our children eventually call
|
||||
* getpwnam() in do_command.c's child_process to verify MAILTO=,
|
||||
* they will screw us up (and v-v).
|
||||
*/
|
||||
endpwent();
|
||||
|
||||
/* whatever's left in the old database is now junk.
|
||||
*/
|
||||
Debug(DLOAD, ("unlinking old database:\n"))
|
||||
for (u = old_db->head; u != NULL; u = nu) {
|
||||
Debug(DLOAD, ("\t%s\n", u->name))
|
||||
nu = u->next;
|
||||
unlink_user(old_db, u);
|
||||
free_user(u);
|
||||
}
|
||||
|
||||
/* overwrite the database control block with the new one.
|
||||
*/
|
||||
*old_db = new_db;
|
||||
Debug(DLOAD, ("load_database is done\n"))
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
link_user(db, u)
|
||||
register cron_db *db;
|
||||
register user *u;
|
||||
{
|
||||
if (db->head == NULL)
|
||||
db->head = u;
|
||||
if (db->tail)
|
||||
db->tail->next = u;
|
||||
u->prev = db->tail;
|
||||
u->next = NULL;
|
||||
db->tail = u;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
unlink_user(db, u)
|
||||
register cron_db *db;
|
||||
register user *u;
|
||||
{
|
||||
if (u->prev == NULL)
|
||||
db->head = u->next;
|
||||
else
|
||||
u->prev->next = u->next;
|
||||
|
||||
if (u->next == NULL)
|
||||
db->tail = u->prev;
|
||||
else
|
||||
u->next->prev = u->prev;
|
||||
}
|
||||
|
||||
|
||||
user *
|
||||
find_user(db, name)
|
||||
cron_db *db;
|
||||
register char *name;
|
||||
{
|
||||
char *env_get();
|
||||
register user *u;
|
||||
|
||||
for (u = db->head; u != NULL; u = u->next)
|
||||
if (!strcmp(u->name, name))
|
||||
break;
|
||||
return u;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
process_crontab(uname, fname, tabname, statbuf, new_db, old_db)
|
||||
char *uname;
|
||||
char *fname;
|
||||
char *tabname;
|
||||
struct stat *statbuf;
|
||||
cron_db *new_db;
|
||||
cron_db *old_db;
|
||||
{
|
||||
struct passwd *pw = NULL;
|
||||
int crontab_fd = OK - 1;
|
||||
register user *u;
|
||||
|
||||
if (strcmp(fname, "*system*") && !(pw = getpwnam(uname))) {
|
||||
/* file doesn't have a user in passwd file.
|
||||
*/
|
||||
log_it(fname, getpid(), "ORPHAN", "no passwd entry");
|
||||
goto next_crontab;
|
||||
}
|
||||
|
||||
if ((crontab_fd = open(tabname, O_RDONLY, 0)) < OK) {
|
||||
/* crontab not accessible?
|
||||
*/
|
||||
log_it(fname, getpid(), "CAN'T OPEN", tabname);
|
||||
goto next_crontab;
|
||||
}
|
||||
|
||||
if (fstat(crontab_fd, statbuf) < OK) {
|
||||
log_it(fname, getpid(), "FSTAT FAILED", tabname);
|
||||
goto next_crontab;
|
||||
}
|
||||
|
||||
Debug(DLOAD, ("\t%s:", fname))
|
||||
u = find_user(old_db, fname);
|
||||
if (u != NULL) {
|
||||
/* if crontab has not changed since we last read it
|
||||
* in, then we can just use our existing entry.
|
||||
*/
|
||||
if (u->mtime == statbuf->st_mtime) {
|
||||
Debug(DLOAD, (" [no change, using old data]"))
|
||||
unlink_user(old_db, u);
|
||||
link_user(new_db, u);
|
||||
goto next_crontab;
|
||||
}
|
||||
|
||||
/* before we fall through to the code that will reload
|
||||
* the user, let's deallocate and unlink the user in
|
||||
* the old database. This is more a point of memory
|
||||
* efficiency than anything else, since all leftover
|
||||
* users will be deleted from the old database when
|
||||
* we finish with the crontab...
|
||||
*/
|
||||
Debug(DLOAD, (" [delete old data]"))
|
||||
unlink_user(old_db, u);
|
||||
free_user(u);
|
||||
log_it(fname, getpid(), "RELOAD", tabname);
|
||||
}
|
||||
u = load_user(crontab_fd, pw, fname);
|
||||
if (u != NULL) {
|
||||
u->mtime = statbuf->st_mtime;
|
||||
link_user(new_db, u);
|
||||
}
|
||||
|
||||
next_crontab:
|
||||
if (crontab_fd >= OK) {
|
||||
Debug(DLOAD, (" [done]\n"))
|
||||
close(crontab_fd);
|
||||
}
|
||||
}
|
||||
433
src/cmd/cron/do_command.c
Executable file
433
src/cmd/cron/do_command.c
Executable file
@@ -0,0 +1,433 @@
|
||||
/* Copyright 1988,1990,1993,1994 by Paul Vixie
|
||||
* All rights reserved
|
||||
*
|
||||
* Distribute freely, except: don't remove my name from the source or
|
||||
* documentation (don't take credit for my work), mark your changes (don't
|
||||
* get me blamed for your possible bugs), don't alter or remove this
|
||||
* notice. May be sold if buildable source is provided to buyer. No
|
||||
* warrantee of any kind, express or implied, is included with this
|
||||
* software; use at your own risk, responsibility for damages (if any) to
|
||||
* anyone resulting from the use of this software rests entirely with the
|
||||
* user.
|
||||
*
|
||||
* Send bug reports, bug fixes, enhancements, requests, flames, etc., and
|
||||
* I'll try to keep a version up to date. I can be reached as follows:
|
||||
* Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
|
||||
*/
|
||||
|
||||
#if !defined(lint) && !defined(LINT)
|
||||
static char rcsid[] = "$Id: do_command.c,v 2.12 1994/01/15 20:43:43 vixie Exp $";
|
||||
#endif
|
||||
|
||||
#include "cron.h"
|
||||
#include <sys/signal.h>
|
||||
#if defined(SYSLOG)
|
||||
# include <syslog.h>
|
||||
#endif
|
||||
|
||||
static void child_process __P((entry *, user *));
|
||||
|
||||
void
|
||||
do_command(e, u)
|
||||
entry *e;
|
||||
user *u;
|
||||
{
|
||||
Debug(DPROC, ("[%d] do_command(%s, (%s,%d,%d))\n",
|
||||
getpid(), e->cmd, u->name, e->uid, e->gid))
|
||||
|
||||
/* fork to become asynchronous -- parent process is done immediately,
|
||||
* and continues to run the normal cron code, which means return to
|
||||
* tick(). the child and grandchild don't leave this function, alive.
|
||||
*
|
||||
* vfork() is unsuitable, since we have much to do, and the parent
|
||||
* needs to be able to run off and fork other processes.
|
||||
*/
|
||||
switch (fork()) {
|
||||
case -1:
|
||||
log_it("CRON",getpid(),"error","can't fork");
|
||||
break;
|
||||
case 0:
|
||||
/* child process */
|
||||
acquire_daemonlock(1);
|
||||
child_process(e, u);
|
||||
Debug(DPROC, ("[%d] child process done, exiting\n", getpid()))
|
||||
_exit(OK_EXIT);
|
||||
break;
|
||||
default:
|
||||
/* parent process */
|
||||
break;
|
||||
}
|
||||
Debug(DPROC, ("[%d] main process returning to work\n", getpid()))
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
child_process(e, u)
|
||||
entry *e;
|
||||
user *u;
|
||||
{
|
||||
int stdin_pipe[2], stdout_pipe[2];
|
||||
register char *input_data;
|
||||
char *usernm, *mailto;
|
||||
register int ch;
|
||||
int children = 0, escaped;
|
||||
|
||||
Debug(DPROC, ("[%d] child_process('%s')\n", getpid(), e->cmd))
|
||||
|
||||
/* mark ourselves as different to PS command watchers by upshifting
|
||||
* our program name. This has no effect on some kernels.
|
||||
*/
|
||||
for (input_data = ProgramName; ch = *input_data; input_data++)
|
||||
*input_data = (islower(ch) ? toupper(ch) : ch);
|
||||
|
||||
/* discover some useful and important environment settings
|
||||
*/
|
||||
usernm = env_get("LOGNAME", e->envp);
|
||||
mailto = env_get("MAILTO", e->envp);
|
||||
|
||||
/* our parent is watching for our death by catching SIGCHLD. we
|
||||
* do not care to watch for our children's deaths this way -- we
|
||||
* use wait() explictly. so we have to disable the signal (which
|
||||
* was inherited from the parent).
|
||||
*/
|
||||
(void) signal(SIGCHLD, SIG_IGN);
|
||||
|
||||
/* create some pipes to talk to our future child
|
||||
*/
|
||||
pipe(stdin_pipe); /* child's stdin */
|
||||
pipe(stdout_pipe); /* child's stdout */
|
||||
|
||||
/* since we are a forked process, we can diddle the command string
|
||||
* we were passed -- nobody else is going to use it again, right?
|
||||
*
|
||||
* if a % is present in the command, previous characters are the
|
||||
* command, and subsequent characters are the additional input to
|
||||
* the command. Subsequent %'s will be transformed into newlines,
|
||||
* but that happens later.
|
||||
*/
|
||||
escaped = FALSE;
|
||||
for (input_data = e->cmd; ch = *input_data; input_data++) {
|
||||
if (escaped) {
|
||||
escaped = FALSE;
|
||||
continue;
|
||||
}
|
||||
if (ch == '\\') {
|
||||
escaped = TRUE;
|
||||
continue;
|
||||
}
|
||||
if (ch == '%') {
|
||||
*input_data++ = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* fork again, this time so we can exec the user's command.
|
||||
*/
|
||||
switch (vfork()) {
|
||||
case -1:
|
||||
log_it("CRON",getpid(),"error","can't vfork");
|
||||
exit(ERROR_EXIT);
|
||||
/*NOTREACHED*/
|
||||
case 0:
|
||||
Debug(DPROC, ("[%d] grandchild process Vfork()'ed\n",
|
||||
getpid()))
|
||||
|
||||
/* write a log message. we've waited this long to do it
|
||||
* because it was not until now that we knew the PID that
|
||||
* the actual user command shell was going to get and the
|
||||
* PID is part of the log message.
|
||||
*/
|
||||
/*local*/{
|
||||
char *x = mkprints((u_char *)e->cmd, strlen(e->cmd));
|
||||
|
||||
log_it(usernm, getpid(), "CMD", x);
|
||||
free(x);
|
||||
}
|
||||
|
||||
/* that's the last thing we'll log. close the log files.
|
||||
*/
|
||||
#ifdef SYSLOG
|
||||
closelog();
|
||||
#endif
|
||||
|
||||
/* get new pgrp, void tty, etc.
|
||||
*/
|
||||
(void) setsid();
|
||||
|
||||
/* close the pipe ends that we won't use. this doesn't affect
|
||||
* the parent, who has to read and write them; it keeps the
|
||||
* kernel from recording us as a potential client TWICE --
|
||||
* which would keep it from sending SIGPIPE in otherwise
|
||||
* appropriate circumstances.
|
||||
*/
|
||||
close(stdin_pipe[WRITE_PIPE]);
|
||||
close(stdout_pipe[READ_PIPE]);
|
||||
|
||||
/* grandchild process. make std{in,out} be the ends of
|
||||
* pipes opened by our daddy; make stderr go to stdout.
|
||||
*/
|
||||
close(STDIN); dup2(stdin_pipe[READ_PIPE], STDIN);
|
||||
close(STDOUT); dup2(stdout_pipe[WRITE_PIPE], STDOUT);
|
||||
close(STDERR); dup2(STDOUT, STDERR);
|
||||
|
||||
/* close the pipes we just dup'ed. The resources will remain.
|
||||
*/
|
||||
close(stdin_pipe[READ_PIPE]);
|
||||
close(stdout_pipe[WRITE_PIPE]);
|
||||
|
||||
/* set our directory, uid and gid. Set gid first, since once
|
||||
* we set uid, we've lost root privledges.
|
||||
*/
|
||||
setgid(e->gid);
|
||||
initgroups(env_get("LOGNAME", e->envp), e->gid);
|
||||
setuid(e->uid); /* we aren't root after this... */
|
||||
chdir(env_get("HOME", e->envp));
|
||||
|
||||
/* exec the command.
|
||||
*/
|
||||
{
|
||||
char *shell = env_get("SHELL", e->envp);
|
||||
|
||||
# if DEBUGGING
|
||||
if (DebugFlags & DTEST) {
|
||||
fprintf(stderr,
|
||||
"debug DTEST is on, not exec'ing command.\n");
|
||||
fprintf(stderr,
|
||||
"\tcmd='%s' shell='%s'\n", e->cmd, shell);
|
||||
_exit(OK_EXIT);
|
||||
}
|
||||
# endif /*DEBUGGING*/
|
||||
execle(shell, shell, "-c", e->cmd, (char *)0, e->envp);
|
||||
fprintf(stderr, "execl: couldn't exec `%s'\n", shell);
|
||||
perror("execl");
|
||||
_exit(ERROR_EXIT);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* parent process */
|
||||
break;
|
||||
}
|
||||
|
||||
children++;
|
||||
|
||||
/* middle process, child of original cron, parent of process running
|
||||
* the user's command.
|
||||
*/
|
||||
|
||||
Debug(DPROC, ("[%d] child continues, closing pipes\n", getpid()))
|
||||
|
||||
/* close the ends of the pipe that will only be referenced in the
|
||||
* grandchild process...
|
||||
*/
|
||||
close(stdin_pipe[READ_PIPE]);
|
||||
close(stdout_pipe[WRITE_PIPE]);
|
||||
|
||||
/*
|
||||
* write, to the pipe connected to child's stdin, any input specified
|
||||
* after a % in the crontab entry. while we copy, convert any
|
||||
* additional %'s to newlines. when done, if some characters were
|
||||
* written and the last one wasn't a newline, write a newline.
|
||||
*
|
||||
* Note that if the input data won't fit into one pipe buffer (2K
|
||||
* or 4K on most BSD systems), and the child doesn't read its stdin,
|
||||
* we would block here. thus we must fork again.
|
||||
*/
|
||||
|
||||
if (*input_data && fork() == 0) {
|
||||
register FILE *out = fdopen(stdin_pipe[WRITE_PIPE], "w");
|
||||
register int need_newline = FALSE;
|
||||
register int escaped = FALSE;
|
||||
register int ch;
|
||||
|
||||
Debug(DPROC, ("[%d] child2 sending data to grandchild\n", getpid()))
|
||||
|
||||
/* close the pipe we don't use, since we inherited it and
|
||||
* are part of its reference count now.
|
||||
*/
|
||||
close(stdout_pipe[READ_PIPE]);
|
||||
|
||||
/* translation:
|
||||
* \% -> %
|
||||
* % -> \n
|
||||
* \x -> \x for all x != %
|
||||
*/
|
||||
while (ch = *input_data++) {
|
||||
if (escaped) {
|
||||
if (ch != '%')
|
||||
putc('\\', out);
|
||||
} else {
|
||||
if (ch == '%')
|
||||
ch = '\n';
|
||||
}
|
||||
|
||||
if (!(escaped = (ch == '\\'))) {
|
||||
putc(ch, out);
|
||||
need_newline = (ch != '\n');
|
||||
}
|
||||
}
|
||||
if (escaped)
|
||||
putc('\\', out);
|
||||
if (need_newline)
|
||||
putc('\n', out);
|
||||
|
||||
/* close the pipe, causing an EOF condition. fclose causes
|
||||
* stdin_pipe[WRITE_PIPE] to be closed, too.
|
||||
*/
|
||||
fclose(out);
|
||||
|
||||
Debug(DPROC, ("[%d] child2 done sending to grandchild\n", getpid()))
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* close the pipe to the grandkiddie's stdin, since its wicked uncle
|
||||
* ernie back there has it open and will close it when he's done.
|
||||
*/
|
||||
close(stdin_pipe[WRITE_PIPE]);
|
||||
|
||||
children++;
|
||||
|
||||
/*
|
||||
* read output from the grandchild. it's stderr has been redirected to
|
||||
* it's stdout, which has been redirected to our pipe. if there is any
|
||||
* output, we'll be mailing it to the user whose crontab this is...
|
||||
* when the grandchild exits, we'll get EOF.
|
||||
*/
|
||||
|
||||
Debug(DPROC, ("[%d] child reading output from grandchild\n", getpid()))
|
||||
|
||||
/*local*/{
|
||||
register FILE *in = fdopen(stdout_pipe[READ_PIPE], "r");
|
||||
register int ch = getc(in);
|
||||
|
||||
if (ch != EOF) {
|
||||
register FILE *mail;
|
||||
register int bytes = 1;
|
||||
int status = 0;
|
||||
|
||||
Debug(DPROC|DEXT,
|
||||
("[%d] got data (%x:%c) from grandchild\n",
|
||||
getpid(), ch, ch))
|
||||
|
||||
/* get name of recipient. this is MAILTO if set to a
|
||||
* valid local username; USER otherwise.
|
||||
*/
|
||||
if (mailto) {
|
||||
/* MAILTO was present in the environment
|
||||
*/
|
||||
if (!*mailto) {
|
||||
/* ... but it's empty. set to NULL
|
||||
*/
|
||||
mailto = NULL;
|
||||
}
|
||||
} else {
|
||||
/* MAILTO not present, set to USER.
|
||||
*/
|
||||
mailto = usernm;
|
||||
}
|
||||
|
||||
/* if we are supposed to be mailing, MAILTO will
|
||||
* be non-NULL. only in this case should we set
|
||||
* up the mail command and subjects and stuff...
|
||||
*/
|
||||
|
||||
if (mailto) {
|
||||
register char **env;
|
||||
auto char mailcmd[MAX_COMMAND];
|
||||
auto char hostname[MAXHOSTNAMELEN];
|
||||
|
||||
(void) gethostname(hostname, MAXHOSTNAMELEN);
|
||||
(void) sprintf(mailcmd, MAILARGS,
|
||||
MAILCMD, mailto);
|
||||
if (!(mail = cron_popen(mailcmd, "w"))) {
|
||||
perror(MAILCMD);
|
||||
(void) _exit(ERROR_EXIT);
|
||||
}
|
||||
fprintf(mail, "From: root (Cron Daemon)\n");
|
||||
fprintf(mail, "To: %s\n", mailto);
|
||||
fprintf(mail, "Subject: Cron <%s@%s> %s\n",
|
||||
usernm, first_word(hostname, "."),
|
||||
e->cmd);
|
||||
# if defined(MAIL_DATE)
|
||||
fprintf(mail, "Date: %s\n",
|
||||
arpadate(&TargetTime));
|
||||
# endif /* MAIL_DATE */
|
||||
for (env = e->envp; *env; env++)
|
||||
fprintf(mail, "X-Cron-Env: <%s>\n",
|
||||
*env);
|
||||
fprintf(mail, "\n");
|
||||
|
||||
/* this was the first char from the pipe
|
||||
*/
|
||||
putc(ch, mail);
|
||||
}
|
||||
|
||||
/* we have to read the input pipe no matter whether
|
||||
* we mail or not, but obviously we only write to
|
||||
* mail pipe if we ARE mailing.
|
||||
*/
|
||||
|
||||
while (EOF != (ch = getc(in))) {
|
||||
bytes++;
|
||||
if (mailto)
|
||||
putc(ch, mail);
|
||||
}
|
||||
|
||||
/* only close pipe if we opened it -- i.e., we're
|
||||
* mailing...
|
||||
*/
|
||||
|
||||
if (mailto) {
|
||||
Debug(DPROC, ("[%d] closing pipe to mail\n",
|
||||
getpid()))
|
||||
/* Note: the pclose will probably see
|
||||
* the termination of the grandchild
|
||||
* in addition to the mail process, since
|
||||
* it (the grandchild) is likely to exit
|
||||
* after closing its stdout.
|
||||
*/
|
||||
status = cron_pclose(mail);
|
||||
}
|
||||
|
||||
/* if there was output and we could not mail it,
|
||||
* log the facts so the poor user can figure out
|
||||
* what's going on.
|
||||
*/
|
||||
if (mailto && status) {
|
||||
char buf[MAX_TEMPSTR];
|
||||
|
||||
sprintf(buf,
|
||||
"mailed %d byte%s of output but got status 0x%04x\n",
|
||||
bytes, (bytes==1)?"":"s",
|
||||
status);
|
||||
log_it(usernm, getpid(), "MAIL", buf);
|
||||
}
|
||||
|
||||
} /*if data from grandchild*/
|
||||
|
||||
Debug(DPROC, ("[%d] got EOF from grandchild\n", getpid()))
|
||||
|
||||
fclose(in); /* also closes stdout_pipe[READ_PIPE] */
|
||||
}
|
||||
|
||||
/* wait for children to die.
|
||||
*/
|
||||
for (; children > 0; children--)
|
||||
{
|
||||
WAIT_T waiter;
|
||||
PID_T pid;
|
||||
|
||||
Debug(DPROC, ("[%d] waiting for grandchild #%d to finish\n",
|
||||
getpid(), children))
|
||||
pid = wait(&waiter);
|
||||
if (pid < OK) {
|
||||
Debug(DPROC, ("[%d] no more grandchildren--mail written?\n",
|
||||
getpid()))
|
||||
break;
|
||||
}
|
||||
Debug(DPROC, ("[%d] grandchild #%d finished, status=%04x",
|
||||
getpid(), pid, WEXITSTATUS(waiter)))
|
||||
if (WIFSIGNALED(waiter) && WCOREDUMP(waiter))
|
||||
Debug(DPROC, (", dumped core"))
|
||||
Debug(DPROC, ("\n"))
|
||||
}
|
||||
}
|
||||
504
src/cmd/cron/entry.c
Executable file
504
src/cmd/cron/entry.c
Executable file
@@ -0,0 +1,504 @@
|
||||
/* Copyright 1988,1990,1993,1994 by Paul Vixie
|
||||
* All rights reserved
|
||||
*
|
||||
* Distribute freely, except: don't remove my name from the source or
|
||||
* documentation (don't take credit for my work), mark your changes (don't
|
||||
* get me blamed for your possible bugs), don't alter or remove this
|
||||
* notice. May be sold if buildable source is provided to buyer. No
|
||||
* warrantee of any kind, express or implied, is included with this
|
||||
* software; use at your own risk, responsibility for damages (if any) to
|
||||
* anyone resulting from the use of this software rests entirely with the
|
||||
* user.
|
||||
*
|
||||
* Send bug reports, bug fixes, enhancements, requests, flames, etc., and
|
||||
* I'll try to keep a version up to date. I can be reached as follows:
|
||||
* Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
|
||||
*/
|
||||
|
||||
#if !defined(lint) && defined(DOSCCS)
|
||||
static char sccsid[] = "@(#)entry.c 2.12.2 (2.11BSD) 1999/08/05";
|
||||
#endif
|
||||
|
||||
/* vix 26jan87 [RCS'd; rest of log is in RCS file]
|
||||
* vix 01jan87 [added line-level error recovery]
|
||||
* vix 31dec86 [added /step to the from-to range, per bob@acornrc]
|
||||
* vix 30dec86 [written]
|
||||
*/
|
||||
|
||||
#include "cron.h"
|
||||
|
||||
typedef enum ecode {
|
||||
e_none, e_minute, e_hour, e_dom, e_month, e_dow,
|
||||
e_cmd, e_timespec, e_username
|
||||
} ecode_e;
|
||||
|
||||
static char get_list __P((bitstr_t *, int, int, char *[], int, FILE *)),
|
||||
get_range __P((bitstr_t *, int, int, char *[], int, FILE *)),
|
||||
get_number __P((int *, int, char *[], int, FILE *));
|
||||
static int set_element __P((bitstr_t *, int, int, int));
|
||||
|
||||
static char *ecodes[] =
|
||||
{
|
||||
"no error",
|
||||
"bad minute",
|
||||
"bad hour",
|
||||
"bad day-of-month",
|
||||
"bad month",
|
||||
"bad day-of-week",
|
||||
"bad command",
|
||||
"bad time specifier",
|
||||
"bad username",
|
||||
};
|
||||
|
||||
|
||||
void
|
||||
free_entry(e)
|
||||
register entry *e;
|
||||
{
|
||||
free(e->cmd);
|
||||
env_free(e->envp);
|
||||
free(e);
|
||||
}
|
||||
|
||||
|
||||
/* return NULL if eof or syntax error occurs;
|
||||
* otherwise return a pointer to a new entry.
|
||||
*/
|
||||
entry *
|
||||
load_entry(file, error_func, pw, envp)
|
||||
FILE *file;
|
||||
void (*error_func)();
|
||||
register struct passwd *pw;
|
||||
char **envp;
|
||||
{
|
||||
/* this function reads one crontab entry -- the next -- from a file.
|
||||
* it skips any leading blank lines, ignores comments, and returns
|
||||
* EOF if for any reason the entry can't be read and parsed.
|
||||
*
|
||||
* the entry is also parsed here.
|
||||
*
|
||||
* syntax:
|
||||
* user crontab:
|
||||
* minutes hours doms months dows cmd\n
|
||||
* system crontab (/etc/crontab):
|
||||
* minutes hours doms months dows USERNAME cmd\n
|
||||
*/
|
||||
|
||||
ecode_e ecode = e_none;
|
||||
register entry *e;
|
||||
int ch;
|
||||
char cmd[MAX_COMMAND];
|
||||
char envstr[MAX_ENVSTR];
|
||||
|
||||
Debug(DPARS, ("load_entry()...about to eat comments\n"))
|
||||
|
||||
skip_comments(file);
|
||||
|
||||
ch = get_char(file);
|
||||
if (ch == EOF)
|
||||
return NULL;
|
||||
|
||||
/* ch is now the first useful character of a useful line.
|
||||
* it may be an @special or it may be the first character
|
||||
* of a list of minutes.
|
||||
*/
|
||||
|
||||
e = (entry *) calloc(sizeof(entry), sizeof(char));
|
||||
|
||||
if (ch == '@') {
|
||||
/* all of these should be flagged and load-limited; i.e.,
|
||||
* instead of @hourly meaning "0 * * * *" it should mean
|
||||
* "close to the front of every hour but not 'til the
|
||||
* system load is low". Problems are: how do you know
|
||||
* what "low" means? (save me from /etc/cron.conf!) and:
|
||||
* how to guarantee low variance (how low is low?), which
|
||||
* means how to we run roughly every hour -- seems like
|
||||
* we need to keep a history or let the first hour set
|
||||
* the schedule, which means we aren't load-limited
|
||||
* anymore. too much for my overloaded brain. (vix, jan90)
|
||||
* HINT
|
||||
*/
|
||||
ch = get_string(cmd, MAX_COMMAND, file, " \t\n");
|
||||
if (!strcmp("reboot", cmd)) {
|
||||
e->flags |= WHEN_REBOOT;
|
||||
} else if (!strcmp("yearly", cmd) || !strcmp("annually", cmd)){
|
||||
bit_set(e->minute, 0);
|
||||
bit_set(e->hour, 0);
|
||||
bit_set(e->dom, 0);
|
||||
bit_set(e->month, 0);
|
||||
bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
|
||||
} else if (!strcmp("monthly", cmd)) {
|
||||
bit_set(e->minute, 0);
|
||||
bit_set(e->hour, 0);
|
||||
bit_set(e->dom, 0);
|
||||
bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
|
||||
bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
|
||||
} else if (!strcmp("weekly", cmd)) {
|
||||
bit_set(e->minute, 0);
|
||||
bit_set(e->hour, 0);
|
||||
bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
|
||||
bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
|
||||
bit_set(e->dow, 0);
|
||||
} else if (!strcmp("daily", cmd) || !strcmp("midnight", cmd)) {
|
||||
bit_set(e->minute, 0);
|
||||
bit_set(e->hour, 0);
|
||||
bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
|
||||
bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
|
||||
bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
|
||||
} else if (!strcmp("hourly", cmd)) {
|
||||
bit_set(e->minute, 0);
|
||||
bit_set(e->hour, (LAST_HOUR-FIRST_HOUR+1));
|
||||
bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
|
||||
bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
|
||||
bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
|
||||
} else {
|
||||
ecode = e_timespec;
|
||||
goto eof;
|
||||
}
|
||||
} else {
|
||||
Debug(DPARS, ("load_entry()...about to parse numerics\n"))
|
||||
|
||||
ch = get_list(e->minute, FIRST_MINUTE, LAST_MINUTE,
|
||||
PPC_NULL, ch, file);
|
||||
if (ch == EOF) {
|
||||
ecode = e_minute;
|
||||
goto eof;
|
||||
}
|
||||
|
||||
/* hours
|
||||
*/
|
||||
|
||||
ch = get_list(e->hour, FIRST_HOUR, LAST_HOUR,
|
||||
PPC_NULL, ch, file);
|
||||
if (ch == EOF) {
|
||||
ecode = e_hour;
|
||||
goto eof;
|
||||
}
|
||||
|
||||
/* DOM (days of month)
|
||||
*/
|
||||
|
||||
if (ch == '*')
|
||||
e->flags |= DOM_STAR;
|
||||
ch = get_list(e->dom, FIRST_DOM, LAST_DOM,
|
||||
PPC_NULL, ch, file);
|
||||
if (ch == EOF) {
|
||||
ecode = e_dom;
|
||||
goto eof;
|
||||
}
|
||||
|
||||
/* month
|
||||
*/
|
||||
|
||||
ch = get_list(e->month, FIRST_MONTH, LAST_MONTH,
|
||||
MonthNames, ch, file);
|
||||
if (ch == EOF) {
|
||||
ecode = e_month;
|
||||
goto eof;
|
||||
}
|
||||
|
||||
/* DOW (days of week)
|
||||
*/
|
||||
|
||||
if (ch == '*')
|
||||
e->flags |= DOW_STAR;
|
||||
ch = get_list(e->dow, FIRST_DOW, LAST_DOW,
|
||||
DowNames, ch, file);
|
||||
if (ch == EOF) {
|
||||
ecode = e_dow;
|
||||
goto eof;
|
||||
}
|
||||
}
|
||||
|
||||
/* make sundays equivilent */
|
||||
if (bit_test(e->dow, 0) || bit_test(e->dow, 7)) {
|
||||
bit_set(e->dow, 0);
|
||||
bit_set(e->dow, 7);
|
||||
}
|
||||
|
||||
/* ch is the first character of a command, or a username */
|
||||
unget_char(ch, file);
|
||||
|
||||
if (!pw) {
|
||||
char *username = cmd; /* temp buffer */
|
||||
|
||||
Debug(DPARS, ("load_entry()...about to parse username\n"))
|
||||
ch = get_string(username, MAX_COMMAND, file, " \t");
|
||||
|
||||
Debug(DPARS, ("load_entry()...got %s\n",username))
|
||||
if (ch == EOF) {
|
||||
ecode = e_cmd;
|
||||
goto eof;
|
||||
}
|
||||
|
||||
pw = getpwnam(username);
|
||||
if (pw == NULL) {
|
||||
ecode = e_username;
|
||||
goto eof;
|
||||
}
|
||||
Debug(DPARS, ("load_entry()...uid %d, gid %d\n",e->uid,e->gid))
|
||||
}
|
||||
|
||||
e->uid = pw->pw_uid;
|
||||
e->gid = pw->pw_gid;
|
||||
|
||||
/* copy and fix up environment. some variables are just defaults and
|
||||
* others are overrides.
|
||||
*/
|
||||
e->envp = env_copy(envp);
|
||||
if (!env_get("SHELL", e->envp)) {
|
||||
sprintf(envstr, "SHELL=%s", _PATH_BSHELL);
|
||||
e->envp = env_set(e->envp, envstr);
|
||||
}
|
||||
if (!env_get("HOME", e->envp)) {
|
||||
sprintf(envstr, "HOME=%s", pw->pw_dir);
|
||||
e->envp = env_set(e->envp, envstr);
|
||||
}
|
||||
if (!env_get("PATH", e->envp)) {
|
||||
sprintf(envstr, "PATH=%s", _PATH_DEFPATH);
|
||||
e->envp = env_set(e->envp, envstr);
|
||||
}
|
||||
sprintf(envstr, "%s=%s", "LOGNAME", pw->pw_name);
|
||||
e->envp = env_set(e->envp, envstr);
|
||||
sprintf(envstr, "%s=%s", "USER", pw->pw_name);
|
||||
e->envp = env_set(e->envp, envstr);
|
||||
|
||||
Debug(DPARS, ("load_entry()...about to parse command\n"))
|
||||
|
||||
/* Everything up to the next \n or EOF is part of the command...
|
||||
* too bad we don't know in advance how long it will be, since we
|
||||
* need to malloc a string for it... so, we limit it to MAX_COMMAND.
|
||||
* XXX - should use realloc().
|
||||
*/
|
||||
ch = get_string(cmd, MAX_COMMAND, file, "\n");
|
||||
|
||||
/* a file without a \n before the EOF is rude, so we'll complain...
|
||||
*/
|
||||
if (ch == EOF) {
|
||||
ecode = e_cmd;
|
||||
goto eof;
|
||||
}
|
||||
|
||||
/* got the command in the 'cmd' string; save it in *e.
|
||||
*/
|
||||
e->cmd = strdup(cmd);
|
||||
|
||||
Debug(DPARS, ("load_entry()...returning successfully\n"))
|
||||
|
||||
/* success, fini, return pointer to the entry we just created...
|
||||
*/
|
||||
return e;
|
||||
|
||||
eof:
|
||||
free(e);
|
||||
if (ecode != e_none && error_func)
|
||||
(*error_func)(ecodes[(int)ecode]);
|
||||
while (ch != EOF && ch != '\n')
|
||||
ch = get_char(file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static char
|
||||
get_list(bits, low, high, names, ch, file)
|
||||
bitstr_t *bits; /* one bit per flag, default=FALSE */
|
||||
int low, high; /* bounds, impl. offset for bitstr */
|
||||
char *names[]; /* NULL or *[] of names for these elements */
|
||||
int ch; /* current character being processed */
|
||||
register FILE *file; /* file being read */
|
||||
{
|
||||
register int done;
|
||||
|
||||
/* we know that we point to a non-blank character here;
|
||||
* must do a Skip_Blanks before we exit, so that the
|
||||
* next call (or the code that picks up the cmd) can
|
||||
* assume the same thing.
|
||||
*/
|
||||
|
||||
Debug(DPARS|DEXT, ("get_list()...entered\n"))
|
||||
|
||||
/* list = range {"," range}
|
||||
*/
|
||||
|
||||
/* clear the bit string, since the default is 'off'. DONT add an
|
||||
* extra bit here, that's done in the macro!
|
||||
*/
|
||||
bit_nclear(bits, 0, (high-low));
|
||||
|
||||
/* process all ranges
|
||||
*/
|
||||
done = FALSE;
|
||||
while (!done) {
|
||||
ch = get_range(bits, low, high, names, ch, file);
|
||||
if (ch == ',')
|
||||
ch = get_char(file);
|
||||
else
|
||||
done = TRUE;
|
||||
}
|
||||
|
||||
/* exiting. skip to some blanks, then skip over the blanks.
|
||||
*/
|
||||
Skip_Nonblanks(ch, file)
|
||||
Skip_Blanks(ch, file)
|
||||
|
||||
Debug(DPARS|DEXT, ("get_list()...exiting w/ %02x\n", ch))
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
|
||||
static char
|
||||
get_range(bits, low, high, names, ch, file)
|
||||
bitstr_t *bits; /* one bit per flag, default=FALSE */
|
||||
int low, high; /* bounds, impl. offset for bitstr */
|
||||
char *names[]; /* NULL or names of elements */
|
||||
register int ch; /* current character being processed */
|
||||
FILE *file; /* file being read */
|
||||
{
|
||||
/* range = number | number "-" number [ "/" number ]
|
||||
*/
|
||||
|
||||
register int i;
|
||||
auto int num1, num2, num3;
|
||||
|
||||
Debug(DPARS|DEXT, ("get_range()...entering, exit won't show\n"))
|
||||
|
||||
if (ch == '*') {
|
||||
/* '*' means "first-last" but can still be modified by /step
|
||||
*/
|
||||
num1 = low;
|
||||
num2 = high;
|
||||
ch = get_char(file);
|
||||
if (ch == EOF)
|
||||
return EOF;
|
||||
} else {
|
||||
if (EOF == (ch = get_number(&num1, low, names, ch, file)))
|
||||
return EOF;
|
||||
|
||||
if (ch != '-') {
|
||||
/* not a range, it's a single number.
|
||||
*/
|
||||
if (EOF == set_element(bits, low, high, num1))
|
||||
return EOF;
|
||||
return ch;
|
||||
} else {
|
||||
/* eat the dash
|
||||
*/
|
||||
ch = get_char(file);
|
||||
if (ch == EOF)
|
||||
return EOF;
|
||||
|
||||
/* get the number following the dash
|
||||
*/
|
||||
ch = get_number(&num2, low, names, ch, file);
|
||||
if (ch == EOF)
|
||||
return EOF;
|
||||
}
|
||||
}
|
||||
|
||||
/* check for step size
|
||||
*/
|
||||
if (ch == '/') {
|
||||
/* eat the slash
|
||||
*/
|
||||
ch = get_char(file);
|
||||
if (ch == EOF)
|
||||
return EOF;
|
||||
|
||||
/* get the step size -- note: we don't pass the
|
||||
* names here, because the number is not an
|
||||
* element id, it's a step size. 'low' is
|
||||
* sent as a 0 since there is no offset either.
|
||||
*/
|
||||
ch = get_number(&num3, 0, PPC_NULL, ch, file);
|
||||
if (ch == EOF)
|
||||
return EOF;
|
||||
} else {
|
||||
/* no step. default==1.
|
||||
*/
|
||||
num3 = 1;
|
||||
}
|
||||
|
||||
/* range. set all elements from num1 to num2, stepping
|
||||
* by num3. (the step is a downward-compatible extension
|
||||
* proposed conceptually by bob@acornrc, syntactically
|
||||
* designed then implmented by paul vixie).
|
||||
*/
|
||||
for (i = num1; i <= num2; i += num3)
|
||||
if (EOF == set_element(bits, low, high, i))
|
||||
return EOF;
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
|
||||
static char
|
||||
get_number(numptr, low, names, ch, file)
|
||||
int *numptr; /* where does the result go? */
|
||||
int low; /* offset applied to result if symbolic enum used */
|
||||
char *names[]; /* symbolic names, if any, for enums */
|
||||
register int ch; /* current character */
|
||||
FILE *file; /* source */
|
||||
{
|
||||
char temp[MAX_TEMPSTR], *pc;
|
||||
int len, i, all_digits;
|
||||
|
||||
/* collect alphanumerics into our fixed-size temp array
|
||||
*/
|
||||
pc = temp;
|
||||
len = 0;
|
||||
all_digits = TRUE;
|
||||
while (isalnum(ch)) {
|
||||
if (++len >= MAX_TEMPSTR)
|
||||
return EOF;
|
||||
|
||||
*pc++ = ch;
|
||||
|
||||
if (!isdigit(ch))
|
||||
all_digits = FALSE;
|
||||
|
||||
ch = get_char(file);
|
||||
}
|
||||
*pc = '\0';
|
||||
|
||||
/* try to find the name in the name list
|
||||
*/
|
||||
if (names) {
|
||||
for (i = 0; names[i] != NULL; i++) {
|
||||
Debug(DPARS|DEXT,
|
||||
("get_num, compare(%s,%s)\n", names[i], temp))
|
||||
if (!strcasecmp(names[i], temp)) {
|
||||
*numptr = i+low;
|
||||
return ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* no name list specified, or there is one and our string isn't
|
||||
* in it. either way: if it's all digits, use its magnitude.
|
||||
* otherwise, it's an error.
|
||||
*/
|
||||
if (all_digits) {
|
||||
*numptr = atoi(temp);
|
||||
return ch;
|
||||
}
|
||||
|
||||
return EOF;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
set_element(bits, low, high, number)
|
||||
bitstr_t *bits; /* one bit per flag, default=FALSE */
|
||||
int low;
|
||||
int high;
|
||||
int number;
|
||||
{
|
||||
Debug(DPARS|DEXT, ("set_element(?,%d,%d,%d)\n", low, high, number))
|
||||
|
||||
if (number < low || number > high)
|
||||
return EOF;
|
||||
|
||||
bit_set(bits, (number-low));
|
||||
return OK;
|
||||
}
|
||||
178
src/cmd/cron/env.c
Executable file
178
src/cmd/cron/env.c
Executable file
@@ -0,0 +1,178 @@
|
||||
/* Copyright 1988,1990,1993,1994 by Paul Vixie
|
||||
* All rights reserved
|
||||
*
|
||||
* Distribute freely, except: don't remove my name from the source or
|
||||
* documentation (don't take credit for my work), mark your changes (don't
|
||||
* get me blamed for your possible bugs), don't alter or remove this
|
||||
* notice. May be sold if buildable source is provided to buyer. No
|
||||
* warrantee of any kind, express or implied, is included with this
|
||||
* software; use at your own risk, responsibility for damages (if any) to
|
||||
* anyone resulting from the use of this software rests entirely with the
|
||||
* user.
|
||||
*
|
||||
* Send bug reports, bug fixes, enhancements, requests, flames, etc., and
|
||||
* I'll try to keep a version up to date. I can be reached as follows:
|
||||
* Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
|
||||
*/
|
||||
|
||||
#if !defined(lint) && !defined(LINT)
|
||||
static char rcsid[] = "$Id: env.c,v 2.7 1994/01/26 02:25:50 vixie Exp vixie $";
|
||||
#endif
|
||||
|
||||
|
||||
#include "cron.h"
|
||||
|
||||
|
||||
char **
|
||||
env_init()
|
||||
{
|
||||
register char **p = (char **) malloc(sizeof(char **));
|
||||
|
||||
p[0] = NULL;
|
||||
return (p);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
env_free(envp)
|
||||
char **envp;
|
||||
{
|
||||
register char **p;
|
||||
|
||||
for (p = envp; *p; p++)
|
||||
free(*p);
|
||||
free(envp);
|
||||
}
|
||||
|
||||
|
||||
char **
|
||||
env_copy(envp)
|
||||
char **envp;
|
||||
{
|
||||
register int count, i;
|
||||
register char **p;
|
||||
|
||||
for (count = 0; envp[count] != NULL; count++)
|
||||
;
|
||||
p = (char **) malloc((count+1) * sizeof(char *)); /* 1 for the NULL */
|
||||
for (i = 0; i < count; i++)
|
||||
p[i] = strdup(envp[i]);
|
||||
p[count] = NULL;
|
||||
return (p);
|
||||
}
|
||||
|
||||
|
||||
char **
|
||||
env_set(envp, envstr)
|
||||
char **envp;
|
||||
char *envstr;
|
||||
{
|
||||
register int count, found;
|
||||
register char **p;
|
||||
|
||||
/*
|
||||
* count the number of elements, including the null pointer;
|
||||
* also set 'found' to -1 or index of entry if already in here.
|
||||
*/
|
||||
found = -1;
|
||||
for (count = 0; envp[count] != NULL; count++) {
|
||||
if (!strcmp_until(envp[count], envstr, '='))
|
||||
found = count;
|
||||
}
|
||||
count++; /* for the NULL */
|
||||
|
||||
if (found != -1) {
|
||||
/*
|
||||
* it exists already, so just free the existing setting,
|
||||
* save our new one there, and return the existing array.
|
||||
*/
|
||||
free(envp[found]);
|
||||
envp[found] = strdup(envstr);
|
||||
return (envp);
|
||||
}
|
||||
|
||||
/*
|
||||
* it doesn't exist yet, so resize the array, move null pointer over
|
||||
* one, save our string over the old null pointer, and return resized
|
||||
* array.
|
||||
*/
|
||||
p = (char **) realloc((void *) envp,
|
||||
(unsigned) ((count+1) * sizeof(char **)));
|
||||
p[count] = p[count-1];
|
||||
p[count-1] = strdup(envstr);
|
||||
return (p);
|
||||
}
|
||||
|
||||
|
||||
/* return ERR = end of file
|
||||
* FALSE = not an env setting (file was repositioned)
|
||||
* TRUE = was an env setting
|
||||
*/
|
||||
int
|
||||
load_env(envstr, f)
|
||||
char *envstr;
|
||||
FILE *f;
|
||||
{
|
||||
long filepos;
|
||||
int fileline;
|
||||
char name[MAX_TEMPSTR], val[MAX_ENVSTR];
|
||||
int fields;
|
||||
|
||||
filepos = ftell(f);
|
||||
fileline = LineNumber;
|
||||
skip_comments(f);
|
||||
if (EOF == get_string(envstr, MAX_ENVSTR, f, "\n"))
|
||||
return (ERR);
|
||||
|
||||
Debug(DPARS, ("load_env, read <%s>\n", envstr))
|
||||
|
||||
name[0] = val[0] = '\0';
|
||||
fields = sscanf(envstr, "%[^ =] = %[^\n#]", name, val);
|
||||
if (fields != 2) {
|
||||
Debug(DPARS, ("load_env, not 2 fields (%d)\n", fields))
|
||||
fseek(f, filepos, 0);
|
||||
Set_LineNum(fileline);
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
/* 2 fields from scanf; looks like an env setting
|
||||
*/
|
||||
|
||||
/*
|
||||
* process value string
|
||||
*/
|
||||
/*local*/{
|
||||
int len = strdtb(val);
|
||||
|
||||
if (len >= 2) {
|
||||
if (val[0] == '\'' || val[0] == '"') {
|
||||
if (val[len-1] == val[0]) {
|
||||
val[len-1] = '\0';
|
||||
(void) strcpy(val, val+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(void) sprintf(envstr, "%s=%s", name, val);
|
||||
Debug(DPARS, ("load_env, <%s> <%s> -> <%s>\n", name, val, envstr))
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
env_get(name, envp)
|
||||
char *name;
|
||||
char **envp;
|
||||
{
|
||||
register int len = strlen(name);
|
||||
register char *p, *q;
|
||||
|
||||
while (p = *envp++) {
|
||||
if (!(q = strchr(p, '=')))
|
||||
continue;
|
||||
if ((q - p) == len && !strncmp(p, name, len))
|
||||
return (q+1);
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
28
src/cmd/cron/externs.h
Executable file
28
src/cmd/cron/externs.h
Executable file
@@ -0,0 +1,28 @@
|
||||
/* Copyright 1993,1994 by Paul Vixie
|
||||
* All rights reserved
|
||||
*
|
||||
* Distribute freely, except: don't remove my name from the source or
|
||||
* documentation (don't take credit for my work), mark your changes (don't
|
||||
* get me blamed for your possible bugs), don't alter or remove this
|
||||
* notice. May be sold if buildable source is provided to buyer. No
|
||||
* warrantee of any kind, express or implied, is included with this
|
||||
* software; use at your own risk, responsibility for damages (if any) to
|
||||
* anyone resulting from the use of this software rests entirely with the
|
||||
* user.
|
||||
*
|
||||
* Send bug reports, bug fixes, enhancements, requests, flames, etc., and
|
||||
* I'll try to keep a version up to date. I can be reached as follows:
|
||||
* Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/dir.h>
|
||||
|
||||
#define DIR_T struct direct
|
||||
//#define WEXITSTATUS(x) ((x).w_retcode)
|
||||
//#define WTERMSIG(x) ((x).w_termsig)
|
||||
//#define WCOREDUMP(x) ((x).w_coredump)
|
||||
|
||||
extern int setsid __P((void));
|
||||
155
src/cmd/cron/grot/CHANGES
Executable file
155
src/cmd/cron/grot/CHANGES
Executable file
@@ -0,0 +1,155 @@
|
||||
Vixie Cron Changes from V2 to V3
|
||||
Paul Vixie
|
||||
29-Dec-1993
|
||||
|
||||
The crontab command now conforms to POSIX 1003.2. This means that when you
|
||||
install it, if you have any "crontab" command lines floating around in shell
|
||||
scripts (such as /etc/rc or /etc/rc.local), you will need to change them.
|
||||
|
||||
I have integrated several changes made by BSDi for their BSD/386 operating
|
||||
system; these were offerred to me before I started consulting for them, so
|
||||
it is safe to say that they were intended for publication. Most notably,
|
||||
the name of the cron daemon has changed from "crond" to "cron". This was
|
||||
done for compatibility with 4.3BSD. Another change made for the same reason
|
||||
is the ability to read in an /etc/crontab file which has an extra field in
|
||||
each entry, between the time fields and the command. This field is a user
|
||||
name, and it permits the /etc/crontab command to contain commands which are
|
||||
to be run by any user on the system. /etc/crontab is not "installed" via
|
||||
the crontab(1) command; it is automatically read at startup time and it will
|
||||
be reread whenever it changes.
|
||||
|
||||
I also added a "-e" option to crontab(1). Nine people also sent me diffs
|
||||
to add this option, but I had already implemented it on my own. I actually
|
||||
released an interrim version (V2.2, I think) for limited testing, and got a
|
||||
chance to fix a bad security bug in the "-e" option thanks to XXX.
|
||||
|
||||
The daemon used to be extraordinarily sloppy in its use of file descriptors.
|
||||
A heck of a lot of them were left open in spawned jobs, which caused problems
|
||||
for the daemon and also caused problems with the spawned jobs if they were
|
||||
shell scripts since "sh" and "csh" have traditionally used hidden file
|
||||
descriptors to pass information to subshells, and cron was causing them to
|
||||
think they were subshells. If you had trouble with "sh" or "csh" scripts in
|
||||
V2, chances are good that V3 will fix your problems.
|
||||
|
||||
About a dozen people have reminded me that I forgot to initialize
|
||||
"crontab_fd" in database.c. Keith Cantrell was the first, so he gets the
|
||||
point.
|
||||
|
||||
Steve Simmons reminded me that once an account has been deleted from the
|
||||
system, "crontab -u USER -d" will not work. My solution is to suggest to
|
||||
all of you that before you delete a user's account, you first delete that
|
||||
user's crontab file if any. From cron's point of view, usernames can never
|
||||
be treated as arbitrary strings. Either they are valid user names, or they
|
||||
are not. I will not make an exception for the "-d" case, for security
|
||||
reasons that I consider reasonable. It is trivial for a root user to delete
|
||||
the entry by hand if necessary.
|
||||
|
||||
Dan O'Neil reminded me that I forgot to reset "log_fd" in misc.c. A lot of
|
||||
others also reminded me of this, but Dan gets the point. I didn't fix it
|
||||
there, since the real bug was that it should have been open in the parent.
|
||||
|
||||
Peter Kabal reminded me that I forgot to "#ifdef DEBUGGING" some code in
|
||||
misc.c. Hans Trompert actually told me first, but Peter sent the patch so
|
||||
he gets the point.
|
||||
|
||||
Russell Nelson told me that I'd forgotten to "#include <syslog.h>" in misc.c,
|
||||
which explains why a lot of other people complained that it wasn't using
|
||||
syslog even when they configured it that way :-). Steve Simmons told me
|
||||
first, though, so he gets the point.
|
||||
|
||||
An interrim version of the daemon tried to "stat" every file before
|
||||
executing it; this turned out to be a horribly bad idea since finding the
|
||||
name of a file from a shell command is a hard job (that's why we have
|
||||
shells, right?) I removed this bogus code. Dave Burgess gets the point.
|
||||
|
||||
Dennis R. Conley sent a suggestion for MMDF systems, which I've added to the
|
||||
comments in cron.h.
|
||||
|
||||
Mike Heisler noted that I use comments in the CONVERSION file which are
|
||||
documented as illegal in the man pages. Thanks, Mike.
|
||||
|
||||
Irving Wolfe sent me some very cheerful changes for a NeXT system, but I
|
||||
consider the system itself broken and I can't bring myself to #ifdef for
|
||||
something as screwed up as this system seems to be. However, various others
|
||||
did send me smaller patches which appear to have cause cron to build and run
|
||||
correctly on (the latest) NeXT machines, with or without the "-posix" CFLAG.
|
||||
Irving also asked for a per-job MAILTO, and this was finally added later when
|
||||
I integrated the BSD/386 changes contributed by BSDi, and generalized some of
|
||||
the parsing.
|
||||
|
||||
Lots of folks complained that the autogenerated "Date:" header wasn't in
|
||||
ARPA format. I didn't understand this -- either folks will use Sendmail and
|
||||
not generate a Date: at all (since Sendmail will do it), or folks will use
|
||||
something other than Sendmail which won't care about Date: formats. But
|
||||
I've "fixed" it anyway...
|
||||
|
||||
Several people suggested that "*" should be able to take a "/step". One person
|
||||
suggested that "N/step" ought to mean "N-last/step", but that's stretching things
|
||||
a bit far. "*/step" seems quite intuitive to me, so I've added it. Colin Plumb
|
||||
sent in the first and most polite request for this feature.
|
||||
|
||||
As with every release of Cron, BIND, and seemingly everything else I do, one
|
||||
user stands out with the most critical but also the most useful analysis.
|
||||
Cron V3's high score belongs to Peter Holzer, who sent in the nicest looking
|
||||
patch for the "%" interpretation problem and also helped me understand a
|
||||
tricky bit of badness in the "log_fd" problem.
|
||||
|
||||
agulbra@flode.nvg.unit.no wins the honors for being the first to point out the
|
||||
nasty security hole in "crontab -r". 'Nuff said.
|
||||
|
||||
Several folks pointed out that log_it() needed to exist even if logging was
|
||||
disabled. Some day I will create a tool that will compile a subsystem with
|
||||
every possible combination and permutation of #ifdef options, but meanwhile
|
||||
thanks to everybody.
|
||||
|
||||
job_runqueue() was using storage after freeing it, since Jordan told me back
|
||||
in 1983 that C let you do that, and I believed him in 1986 when I wrote all
|
||||
this junk. Linux was the first to die from this error, and the Linux people
|
||||
sent me the most amazing, um, collection of patches for this problem. Thanks
|
||||
for all the fish.
|
||||
|
||||
Jeremy Bettis reminded me that popen() isn't safe. I grabbed Ken Arnold's
|
||||
version of popen/pclose from the ftpd and hacked it to taste. We're safe now,
|
||||
from this at least.
|
||||
|
||||
Branko Lankester sent me a very timely and helpful fix for a looming security
|
||||
problem in my "crontab -e" implementation.
|
||||
|
||||
--------
|
||||
|
||||
Vixie Cron Changes from V1 to V2
|
||||
Paul Vixie
|
||||
8-Feb-1988
|
||||
|
||||
Many changes were made in a rash of activity about six months ago, the exact
|
||||
list of which is no longer clear in my memory. I know that V1 used a file
|
||||
called POKECRON in /usr/spool/cron to tell it that it was time to re-read
|
||||
all the crontab files; V2 uses the modtime the crontab directory as a flag to
|
||||
check out the crontab files; those whose modtime has changed will be re-read,
|
||||
and the others left alone. Note that the crontab(1) command will do a utimes
|
||||
call to make sure the mtime of the dir changes, since the filename/inode will
|
||||
often remain the same after a replacement and the mtime wouldn't change in
|
||||
that case.
|
||||
|
||||
8-Feb-88: made it possible to use much larger environment variable strings.
|
||||
V1 allowed 100 characters; V2 allows 1000. This was needed for PATH
|
||||
variables on some systems. Thanks to Toerless Eckert for this idea.
|
||||
E-mail: UUCP: ...pyramid!fauern!faui10!eckert
|
||||
|
||||
16-Feb-88: added allow/deny, moved /usr/spool/cron/crontabs to
|
||||
/usr/lib/cron/tabs. allow and deny are /usr/lib/cron/{allow,deny},
|
||||
since the sysv naming for this depends on 'at' using the same
|
||||
dir, which would be stupid (hint: use /usr/{lib,spool}/at).
|
||||
|
||||
22-Feb-88: made it read the spool directory for crontabs and look each one
|
||||
up using getpwnam() rather than reading all passwds with getpwent()
|
||||
and trying to open each crontab.
|
||||
|
||||
9-Dec-88: made it sync to :00 after the minute, makes cron predictable.
|
||||
added logging to /var/cron/log.
|
||||
|
||||
14-Apr-90: (actually, changes since December 1989)
|
||||
fixed a number of bugs reported from the net and from John Gilmore.
|
||||
added syslog per Keith Bostic. security features including not
|
||||
being willing to run a command owned or writable by other than
|
||||
the owner of the crontab 9not working well yet)
|
||||
85
src/cmd/cron/grot/CONVERSION
Executable file
85
src/cmd/cron/grot/CONVERSION
Executable file
@@ -0,0 +1,85 @@
|
||||
$Id: CONVERSION,v 2.2 1993/12/28 08:34:43 vixie Exp $
|
||||
|
||||
Conversion of BSD 4.[23] crontab files:
|
||||
|
||||
Edit your current crontab (/usr/lib/crontab) into little pieces, with each
|
||||
users' commands in a different file. This is different on 4.2 and 4.3,
|
||||
but I'll get to that below. The biggest feature of this cron is that you
|
||||
can move 'news' and 'uucp' cron commands into files owned and maintainable
|
||||
by those two users. You also get to rip all the fancy 'su' footwork out
|
||||
of the cron commands. On 4.3, there's no need for the 'su' stuff since the
|
||||
user name appears on each command -- but I'd still rather have separate
|
||||
crontabs with seperate environments and so on.
|
||||
|
||||
Leave the original /usr/lib/crontab! This cron doesn't use it, so you may
|
||||
as well keep it around for a while in case something goes wakko with this
|
||||
fancy version.
|
||||
|
||||
Most commands in most crontabs are run by root, have to run by root, and
|
||||
should continue to be run by root. They still have to be in their own file;
|
||||
I recommend /etc/crontab.src or /usr/adm/crontab.src.
|
||||
|
||||
'uucp's commands need their own file; how about /usr/lib/uucp/crontab.src?
|
||||
'news' also, perhaps in /usr/lib/news/crontab.src...
|
||||
|
||||
I say `how about' and `perhaps' because it really doesn't matter to anyone
|
||||
(except you) where you put the crontab source files. The `crontab' command
|
||||
COPIES them into a protected directory (CRONDIR/SPOOL_DIR in cron.h), named
|
||||
after the user whose crontab it is. If you want to examine, replace, or
|
||||
delete a crontab, the `crontab' command does all of those things. The
|
||||
various `crontab.src' (my suggested name for them) files are just source
|
||||
files---they have to be copied to SPOOLDIR using `crontab' before they'll be
|
||||
executed.
|
||||
|
||||
On 4.2, your crontab might have a few lines like this:
|
||||
|
||||
5 * * * * su uucp < /usr/lib/uucp/uudemon.hr
|
||||
10 4 * * * su uucp < /usr/lib/uucp/uudemon.day
|
||||
15 5 * * 0 su uucp < /usr/lib/uucp/uudemon.wk
|
||||
|
||||
...or like this:
|
||||
|
||||
5 * * * * echo /usr/lib/uucp/uudemon.hr | su uucp
|
||||
10 4 * * * echo /usr/lib/uucp/uudemon.day | su uucp
|
||||
15 5 * * 0 echo /usr/lib/uucp/uudemon.wk | su uucp
|
||||
|
||||
On 4.3, they'd look a little bit better, but not much:
|
||||
|
||||
5 * * * * uucp /usr/lib/uucp/uudemon.hr
|
||||
10 4 * * * uucp /usr/lib/uucp/uudemon.day
|
||||
15 5 * * 0 uucp /usr/lib/uucp/uudemon.wk
|
||||
|
||||
For this cron, you'd create /usr/lib/uucp/crontab.src (or wherever you want
|
||||
to keep uucp's commands) which would look like this:
|
||||
|
||||
# /usr/lib/uucp/crontab.src - uucp's crontab
|
||||
#
|
||||
PATH=/usr/lib/uucp:/bin:/usr/bin
|
||||
SHELL=/bin/sh
|
||||
HOME=/usr/lib/uucp
|
||||
#
|
||||
5 * * * * uudemon.hr
|
||||
10 4 * * * uudemon.day
|
||||
15 5 * * 0 uudemon.wk
|
||||
|
||||
The application to the `news' cron commands (if any) is left for you to
|
||||
figure out. Likewise if there are any other cruddy-looking 'su' commands in
|
||||
your crontab commands, you don't need them anymore: just find a good place
|
||||
to put the `crontab.src' (or whatever you want to call it) file for that
|
||||
user, put the cron commands into it, and install it using the `crontab'
|
||||
command (probably with "-u USERNAME", but see the man page).
|
||||
|
||||
If you run a 4.2-derived cron, you could of course just install your current
|
||||
crontab in toto as root's crontab. It would work exactly the way your
|
||||
current one does, barring the extra steps in installing or changing it.
|
||||
There would still be advantages to this cron, mostly that you get mail if
|
||||
there is any output from your cron commands.
|
||||
|
||||
One note about getting mail from cron: you will probably find, after you
|
||||
install this version of cron, that your cron commands are generating a lot
|
||||
of irritating output. The work-around for this is to redirect all EXPECTED
|
||||
output to a per-execution log file, which you can examine if you want to
|
||||
see the output from the "last time" a command was executed; if you get any
|
||||
UNEXPECTED output, it will be mailed to you. This takes a while to get
|
||||
right, but it's amazingly convenient. Trust me.
|
||||
|
||||
84
src/cmd/cron/grot/FEATURES
Executable file
84
src/cmd/cron/grot/FEATURES
Executable file
@@ -0,0 +1,84 @@
|
||||
$Id: FEATURES,v 2.1 1993/12/28 08:34:43 vixie Exp $
|
||||
|
||||
Features of Vixie's cron relative to BSD 4.[23] and SysV crons:
|
||||
|
||||
-- Environment variables can be set in each crontab. SHELL, USER,
|
||||
LOGNAME, and HOME are set from the user's passwd entry; all except
|
||||
USER can be changed in the crontab. PATH is especially useful to
|
||||
set there. TZ can be set, but cron ignores it other than passing
|
||||
it on through to the commands it runs. Format is
|
||||
|
||||
variable=value
|
||||
|
||||
Blanks surrounding the '=' will be eaten; other blanks in value are
|
||||
okay. Leading or trailing blanks can be preserved by quoting, single
|
||||
or double quotes are okay, just so they match.
|
||||
|
||||
PATH=.:/bin:/usr/bin
|
||||
SHELL=/bin/sh
|
||||
FOOBAR = this is a long blanky example
|
||||
|
||||
Above, FOOBAR would get "this is a long blanky example" as its value.
|
||||
|
||||
SHELL and HOME will be used when it's time to run a command; if
|
||||
you don't set them, HOME defaults to your /etc/passwd entry
|
||||
and SHELL defaults to /bin/sh.
|
||||
|
||||
MAILTO, if set to the login name of a user on your system, will be the
|
||||
person that cron mails the output of commands in that crontab. This is
|
||||
useful if you decide on BINMAIL when configuring cron.h, since binmail
|
||||
doesn't know anything about aliasing.
|
||||
|
||||
-- Weekdays can be specified by name. Case is not significant, but only
|
||||
the first three letters should be specified.
|
||||
|
||||
-- Months can likewise be specified by name. Three letters only.
|
||||
|
||||
-- Ranges and lists can be mixed. Standard crons won't allow '1,3-5'.
|
||||
|
||||
-- Ranges can specify 'step' values. '10-16/2' is like '10,12,14,16'.
|
||||
|
||||
-- Sunday is both day 0 and day 7 -- apparently BSD and ATT disagree
|
||||
about this.
|
||||
|
||||
-- Each user gets their own crontab file. This is a win over BSD 4.2,
|
||||
where only root has one, and over BSD 4.3, where they made the crontab
|
||||
format incompatible and although the commands can be run by non-root
|
||||
uid's, root is still the only one who can edit the crontab file. This
|
||||
feature mimics the SysV cron.
|
||||
|
||||
-- The 'crontab' command is loosely compatible with SysV, but has more
|
||||
options which just generally make more sense. Running crontab with
|
||||
no arguments will print a cute little summary of the command syntax.
|
||||
|
||||
-- Comments and blank lines are allowed in the crontab file. Comments
|
||||
must be on a line by themselves; leading whitespace is ignored, and
|
||||
a '#' introduces the comment.
|
||||
|
||||
-- (big win) If the `crontab' command changes anything in any crontab,
|
||||
the 'cron' daemon will reload all the tables before running the
|
||||
next iteration. In some crons, you have to kill and restart the
|
||||
daemon whenever you change a crontab. In other crons, the crontab
|
||||
file is reread and reparsed every minute even if it didn't change.
|
||||
|
||||
-- In order to support the automatic reload, the crontab files are not
|
||||
readable or writable except by 'crontab' or 'cron'. This is not a
|
||||
problem, since 'crontab' will let you do pretty much whatever you
|
||||
want to your own crontab, or if you are root, to anybody's crontab.
|
||||
|
||||
-- If any output is generated by a command (on stdout OR stderr), it will
|
||||
be mailed to the owner of the crontab that contained the command (or
|
||||
MAILTO, see discussion of environment variables, above). The headers
|
||||
of the mail message will include the command that was run, and a
|
||||
complete list of the environment that was passed to it, which will
|
||||
contain (at least) the USER (LOGNAME on SysV), HOME, and SHELL.
|
||||
|
||||
-- the dom/dow situation is odd. '* * 1,15 * Sun' will run on the
|
||||
first and fifteenth AND every Sunday; '* * * * Sun' will run *only*
|
||||
on Sundays; '* * 1,15 * *' will run *only* the 1st and 15th. this
|
||||
is why we keep 'e->dow_star' and 'e->dom_star'. I didn't think up
|
||||
this behaviour; it's how cron has always worked but the documentation
|
||||
hasn't been very clear. I have been told that some AT&T crons do not
|
||||
act this way and do the more reasonable thing, which is (IMHO) to "or"
|
||||
the various field-matches together. In that sense this cron may not
|
||||
be completely similar to some AT&T crons.
|
||||
87
src/cmd/cron/grot/INSTALL
Executable file
87
src/cmd/cron/grot/INSTALL
Executable file
@@ -0,0 +1,87 @@
|
||||
/* Copyright 1993,1994 by Paul Vixie
|
||||
* All rights reserved
|
||||
*
|
||||
* Distribute freely, except: don't remove my name from the source or
|
||||
* documentation (don't take credit for my work), mark your changes (don't
|
||||
* get me blamed for your possible bugs), don't alter or remove this
|
||||
* notice. May be sold if buildable source is provided to buyer. No
|
||||
* warrantee of any kind, express or implied, is included with this
|
||||
* software; use at your own risk, responsibility for damages (if any) to
|
||||
* anyone resulting from the use of this software rests entirely with the
|
||||
* user.
|
||||
*
|
||||
* Send bug reports, bug fixes, enhancements, requests, flames, etc., and
|
||||
* I'll try to keep a version up to date. I can be reached as follows:
|
||||
* Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
|
||||
*/
|
||||
|
||||
$Id: INSTALL,v 2.5 1994/01/15 20:43:43 vixie Exp $
|
||||
|
||||
Read the comments at the top of the Makefile, then edit the area marked
|
||||
'configurable stuff'.
|
||||
|
||||
Edit config.h. The stuff I expect you to change is down a bit from the
|
||||
top of the file, but it's clearly marked. Also look at pathnames.h.
|
||||
|
||||
You don't have to create the /var/cron or /var/cron/tabs directories, since
|
||||
both the daemon and the `crontab' program will do this the first time they
|
||||
run if they don't exist. You do need to have a /var, though -- just "mkdir
|
||||
/var" if you don't have one, or you can "mkdir /usr/var; ln -s /usr/var /var"
|
||||
if you expect your /var to have a lot of stuff in it.
|
||||
|
||||
You will also need /usr/local/etc and /usr/local/bin directories unless you
|
||||
change the Makefile. These will have to be created by hand, but if you are
|
||||
a long-time Usenet user you probably have them already. /usr/local/man is
|
||||
where I keep my man pages, but I have the source for `man' and you probably
|
||||
do not. Therefore you may have to put the man pages into /usr/man/manl,
|
||||
which will be hard since there will be name collisions. (Note that the man
|
||||
command was originally written by Bill Joy before he left Berkeley, and it
|
||||
contains no AT&T code, so it is in UUNET's archive of freely-distributable
|
||||
BSD code.)
|
||||
|
||||
LINUX note: /usr/include/paths.h on some linux systems shows _PATH_SENDMAIL
|
||||
to be /usr/bin/sendmail even though sendmail is installed in /usr/lib.
|
||||
you should check this out.
|
||||
|
||||
say:
|
||||
make all
|
||||
|
||||
su and say:
|
||||
make install
|
||||
|
||||
Note that if I can get you to "su and say" something just by asking, you have
|
||||
a very serious security problem on your system and you should look into it.
|
||||
|
||||
Edit your /usr/lib/crontab file into little pieces -- see the CONVERSION file
|
||||
for help on this.
|
||||
|
||||
Use the `crontab' command to install all the little pieces you just created.
|
||||
Some examples (see below before trying any of these!)
|
||||
|
||||
crontab -u uucp -r /usr/lib/uucp/crontab.src
|
||||
crontab -u news -r /usr/lib/news/crontab.src
|
||||
crontab -u root -r /usr/adm/crontab.src
|
||||
|
||||
Notes on above examples: (1) the .src files are copied at the time the
|
||||
command is issued; changing the source files later will have no effect until
|
||||
they are reinstalled with another `crontab -r' command. (2) The crontab
|
||||
command will affect the crontab of the person using the command unless `-u
|
||||
USER' is given; `-u' only works for root. When using most `su' commands
|
||||
under most BSD's, `crontab' will still think of you as yourself even though
|
||||
you may think of yourself as root -- so use `-u' liberally. (3) the `-r'
|
||||
option stands for `replace'; check the man page for crontab(1) for other
|
||||
possibilities.
|
||||
|
||||
Kill your existing cron daemon -- do `ps aux' and look for /etc/cron.
|
||||
|
||||
Edit your /etc/rc or /etc/rc.local, looking for the line that starts up
|
||||
/etc/cron. Comment it out and add a line to start the new cron daemon
|
||||
-- usually /usr/local/etc/cron, unless you changed it in the Makefile.
|
||||
|
||||
Start up this cron daemon yourself as root. Just type /usr/local/etc/cron
|
||||
(or whatever); no '&' is needed since the daemon forks itself and the
|
||||
process you executed returns immediately.
|
||||
|
||||
ATT notes: for those people unfortunate enough to be stuck on a AT&T UNIX,
|
||||
you will need the public-domain "libndir", found in the B News source and in
|
||||
any comp.sources.unix archive. You will also need to hack the code some.
|
||||
475
src/cmd/cron/grot/MAIL
Executable file
475
src/cmd/cron/grot/MAIL
Executable file
@@ -0,0 +1,475 @@
|
||||
[ this is really old mail that came to me in response to my 1986 posting
|
||||
to usenet asking for feature suggestions before releasing the first
|
||||
version of cron. it is presented here for its entertainment value.
|
||||
--vix ]
|
||||
|
||||
$Id: MAIL,v 1.1 1993/12/28 08:30:36 vixie Exp $
|
||||
|
||||
From ptsfa!lll-crg!ames!acornrc!bob Wed Dec 31 10:07:08 1986
|
||||
Date: Wed, 31 Dec 86 08:59:31 pst
|
||||
From: lll-crg!ames!acornrc!bob (Bob Weissman)
|
||||
To: ptsfa!vixie!paul
|
||||
Status: RO
|
||||
|
||||
Sure, here's a suggestion: I'd like to be able to run a program, say,
|
||||
every two hours. Current cron requires me to write
|
||||
0,2,4,6,8,10,12,14,16,18,20,22 in the hours field. How about a notation
|
||||
to handle this more elegantly?
|
||||
|
||||
<< Okay, I've allowed 0-22/2 as a means of handling this.
|
||||
The time specification for my cron is as follows:
|
||||
specification = range {"," range}
|
||||
range = (start "-" finish ["/" step]) | single-unit
|
||||
This allows "1,3,5-7", which the current cron doesn't (it won't
|
||||
do a range inside a list), and handles your specific need. >>
|
||||
|
||||
From drw@mit-eddie Wed Dec 31 18:25:27 1986
|
||||
Date: Wed, 31 Dec 86 14:28:19 est
|
||||
From: drw@mit-eddie (Dale Worley)
|
||||
To: mit-eddie!vixie!paul
|
||||
Status: RO
|
||||
|
||||
We have a lot of lines in our crontab of the form
|
||||
|
||||
00 12 * * * su user < /usr/users/user/script.file
|
||||
|
||||
This barfs (silently!) on our system (Dec Ultrix 1.2 == 4.2bsd) if
|
||||
user's shell is csh. This, I am told, is because csh requires that
|
||||
the environment be set up in certain ways, which cron doesn't do.
|
||||
(Actually, I believe, it is because /etc/rc, which runs cron, doesn't
|
||||
set up the environment enough for csh to run, and cron just inherits
|
||||
the situation.) Anyway, the point is that if you find out what csh
|
||||
really needs in its environment, you might want to set up cron to
|
||||
provide some reasonable defaults (if it isn't supplied by cron's
|
||||
parent). Also, could you tell me what csh needs, if you find out, so
|
||||
we can hack our /etc/rc?
|
||||
|
||||
<< well, the environment IS a problem. processes that cron forks
|
||||
will inherit the environment of the person who ran the cron
|
||||
daemon... I plan to edit out such useless things as TERMCAP,
|
||||
TERM, and the like; supply correct values for HOME, USER, CWD,
|
||||
and whatever else comes to mind. I'll make sure csh works... >>
|
||||
From ptsfa!ames!seismo!dgis!generous Thu Jan 1 07:33:17 1987
|
||||
Date: Thu Jan 1 10:29:20 1987
|
||||
From: ames!seismo!dgis!generous (Curtis Generous)
|
||||
To: nike!ptsfa!vixie!paul
|
||||
Status: RO
|
||||
|
||||
Paul:
|
||||
|
||||
One of the limitations of the present versions of cron is the lack
|
||||
of the capability of specifying a way to execute a command every
|
||||
n units of time.
|
||||
|
||||
Here is a good example:
|
||||
|
||||
# Present method to start up uucico
|
||||
02,12,22,32,42,52 * * * * exec /usr/lib/uucp/uucico -r1
|
||||
|
||||
# New method ?? (the ':' here is just one possibility for syntax)
|
||||
02:10 * * * * exec /usr/lib/uucp/uucico -r1
|
||||
|
||||
This method would prove very helpful for those programs that get started
|
||||
every few minutes, making the entry long and not easily readable. The first
|
||||
number would specify the base time, and the second number the repetition
|
||||
interval.
|
||||
|
||||
<< Good idea, but bob@acornrc beat you to it. I used '/' instead of
|
||||
':'. This is my personal preference, and seems intuitive when you
|
||||
think of the divide operator in C... Does anyone have a preference? >>
|
||||
|
||||
From ptsfa!lll-lcc!seismo!decuac!c3pe!c3engr!charles Thu Jan 1 17:04:24 1987
|
||||
From: lll-lcc!seismo!c3pe!c3engr!charles (Charles Green)
|
||||
To: c3pe!decuac!dolqci!vrdxhq!seismo!lll-lcc!ptsfa!vixie!paul
|
||||
Date: Thu Jan 1 19:22:47 1987
|
||||
Status: RO
|
||||
|
||||
Well, this isn't a compatible extension, but I have in times past wondered
|
||||
about a facility to let you start a process at intervals of, say, 17 minutes,
|
||||
instead of particular minutes out of each hour.
|
||||
|
||||
<< This was a popular request! >>
|
||||
|
||||
From seismo!uwvax!astroatc!nicmad!norvax!mann Sun Jan 4 13:04:01 1987
|
||||
Date: Fri, 2 Jan 87 09:23:53 cst
|
||||
From: lll-lcc!seismo!uwvax!astroatc!nicmad!norvax!mann (Tom Mann)
|
||||
To: ptsfa!vixie!paul
|
||||
Status: RO
|
||||
|
||||
I'm not sure if it is in cron (either SysV or BSD ... if it is, I haven't
|
||||
figured it out ) but a comment feature would SURE BE NICE!.
|
||||
There are times when I want to comment out an entry
|
||||
for a period of time; it might also make it a lot more legible.
|
||||
|
||||
<< My cron allows blank lines and standard #-type comments. I know
|
||||
that one BSD4.2 cron I've used had it. I don't know about SysV. >>
|
||||
|
||||
From ptsfa!hoptoad!hugh Mon Jan 5 10:26:46 1987
|
||||
Date: Mon, 5 Jan 87 01:22:17 PST
|
||||
From: hoptoad!hugh (Hugh Daniel)
|
||||
To: ptsfa!vixie!paul
|
||||
Status: RO
|
||||
|
||||
Hi, I do have a BIG one that I would like. I want to log ALL output
|
||||
from command lines into a file for each line. Thus I might have a chance
|
||||
of finding out why my crontab entry did not work.
|
||||
This would seem to work best if done by cron, as it is now I have a google
|
||||
of shell scripts laying about just to put the error output where I can see
|
||||
it.
|
||||
|
||||
<< My cron (and the SysV cron) will send mail to the owner of the
|
||||
particular crontab file if a command generates any output on stdout
|
||||
or stderr. This can be irritating, but if you write a script such
|
||||
that any output means a problem occurred, you can avoid most logfile
|
||||
needs, and not generate mail except in unforeseen circumstances. >>
|
||||
|
||||
From ptsfa!dual!ucbvax!ihnp4!anvil!es!Robert_Toxen Mon Jan 5 13:08:46 1987
|
||||
From: dual!ucbvax!ihnp4!anvil!es!Robert_Toxen
|
||||
Date: Fri, 2 Jan 87 14:25:29 EST
|
||||
To: anvil!ihnp4!ucbvax!dual!ptsfa!vixie!paul
|
||||
Status: RO
|
||||
|
||||
Here are some suggestions:
|
||||
1. Run it through the C preprocessor via "/lib/<whatever>".
|
||||
|
||||
<< hmmm. this seems of limited utility, and if you really wanted
|
||||
to do it that way, you could do it yourself (since users can
|
||||
write to their own crontab files). I'll add '-' (read stdin)
|
||||
to the crontab installer program to facilitate this. >>
|
||||
|
||||
2. Allow specifying every Nth day of week, i.e., every second Wednesday.
|
||||
I did this to calendar by separating the day of week (Wed=4, which one
|
||||
to start on and N with slashes). I took modulo the day of year as a
|
||||
starting point so that someone with a desk calendar documenting such
|
||||
things can easily determine the offset (second number). I did this
|
||||
while at SGI; alas I don't have a copy of the code.
|
||||
|
||||
<< I can see how this could be useful, but I'm not sure how I'd
|
||||
implement it. Cron currently doesn't keep track of the last time
|
||||
a given command was run; whether the current Wednesday is the first
|
||||
or second since the command was last run would be pretty hard to
|
||||
figure out. I'd have to keep a database of commands and their
|
||||
execution around, and purge it when the crontab was overwritten.
|
||||
This is too much work for me, but if someone adds it, let me know. >>
|
||||
|
||||
From ptsfa!ames!seismo!cbmvax!devon!paul Tue Jan 6 05:50:17 1987
|
||||
From: ames!seismo!cbmvax!devon!paul
|
||||
To: cbmvax!seismo!nike!ptsfa!vixie!paul
|
||||
Date: Mon Jan 5 09:29:57 1987
|
||||
Status: RO
|
||||
|
||||
One problem that has always plagued me with cron is the assumed ORing.
|
||||
I'd like to see some type of ANDing implemented. I guess I can best
|
||||
describe this by example. Say I have the following line in my crontab
|
||||
file:
|
||||
|
||||
* * 4-31 * 1-6 /usr/bin/command
|
||||
|
||||
What this does is run 'command' on the 4th thru 31st days of the
|
||||
month, AND on Monday thru Saturday; which probably means running it
|
||||
every day of the month (unless Sunday falls on days 1-3). This
|
||||
happens because cron runs the command if the day-of-month OR the
|
||||
day-of-week is true.
|
||||
|
||||
What I'd like to happen with the above line is to run the command ONLY
|
||||
on Monday thru Saturday any time after the 3rd of the month, e.g. if
|
||||
the day-of-month AND the day-of-week are true.
|
||||
|
||||
My proposal to you is to implement some special chars for the first
|
||||
five fields. Examples:
|
||||
|
||||
* * !1-3 * 1-6 /usr/bin/command
|
||||
|
||||
(run command Mon-Sat, but NOT [!] on the first 3 days of the month)
|
||||
|
||||
* * &4-31 * &1-6 /usr/bin/command
|
||||
|
||||
(run command if day-of-month AND day-of-week are true)
|
||||
|
||||
Get the picture? This would be compatable with existing versions of
|
||||
cron (which wouldn't currently be using any special characters, so
|
||||
that old crontabs would be handled correctly).
|
||||
|
||||
<< This message made me aware of the actual boolean expression involved
|
||||
in a crontab entry. I'd assumed that it was
|
||||
(minute && hour && DoM && month && DoW)
|
||||
But it's really
|
||||
(minute && hour && month && (DoM || DoW))
|
||||
|
||||
I can see some value in changing this, but with a fixed order of
|
||||
fields, operators get to be kindof unary, which && and || really
|
||||
aren't. If someone has an idea on a syntax that allows useful
|
||||
variations to the standard (&& && && (||)) default, please suggest. >>
|
||||
|
||||
From bobkat!pedz Tue Jan 6 20:02:10 1987
|
||||
From: pedz@bobkat.UUCP (Pedz Thing)
|
||||
Date: 2 Jan 87 17:34:44 GMT
|
||||
Status: RO
|
||||
|
||||
Log files! It would be nice to be able to specify a log for cron
|
||||
itself and also a log for each program's stdout and stderr to go to.
|
||||
The latter can of course be done with > and 2> but it would be nice if
|
||||
there could be a single line with some sort of pattern like
|
||||
`> /usr/spool/log/%' and the command would be substituted for the %.
|
||||
Another thing which would be nice is to be able to specify which shell
|
||||
to call to give the command to.
|
||||
|
||||
<< Log files are done with mail. The '%' idea could be useful if
|
||||
a different character were used (% is special to cron, see man
|
||||
page); a different directory would have to be chosen, since each
|
||||
user has their own crontab file; and something intelligent would
|
||||
have to be done in the file naming, since the first word of the
|
||||
command might be ambiguous (with other commands). In short, it's
|
||||
too much work. Sorry. >>
|
||||
|
||||
From guy%gorodish@sun Tue Jan 6 20:03:13 1987
|
||||
From: guy%gorodish@sun (Guy Harris)
|
||||
Message-ID: <10944@sun.uucp>
|
||||
Date: 5 Jan 87 12:09:09 GMT
|
||||
References: <429@vixie.UUCP> <359@bobkat.UUCP>
|
||||
Sender: news@sun.uucp
|
||||
Status: RO
|
||||
|
||||
> Another thing which would be nice is to be able to specify which shell
|
||||
> to call to give the command to.
|
||||
|
||||
Well, the obvious choice would be the user's shell, but this wouldn't work
|
||||
for accounts like "uucico".
|
||||
|
||||
<< I use the owning user's shell, and to handle "uucico" I check a
|
||||
list of "acceptable shells" (currently compiled in, does anybody
|
||||
mind?), substituting a default (compiled in) shell if the user's
|
||||
shell isn't on the list.
|
||||
|
||||
BTW, "compiled in" means that it's in a .h file, easily changed
|
||||
during installation, but requiring recompilation to modify. You
|
||||
don't have to go digging through the code to find it... >>
|
||||
|
||||
From qantel!hplabs!ucbvax!mwm@violet.berkeley.edu Tue Jan 6 21:24:48 1987
|
||||
To: hplabs!qantel!vixie!paul (Paul Vixie Esq)
|
||||
Date: 04 Jan 87 00:42:35 PST (Sun)
|
||||
From: Mike Meyer <mwm@violet.berkeley.edu>
|
||||
Status: RO
|
||||
|
||||
<<[Discussion of RMS/FSF, and mwm's GNU Cron deleted]>>
|
||||
|
||||
Oh, yeah - here are the extensions on my cron:
|
||||
|
||||
1) Sunday is both day 0 and day 7, so it complies with both SysV and
|
||||
BSD cron.
|
||||
|
||||
<< Good idea. I did it too, thanks for informing me. >>
|
||||
|
||||
2) At is integrated into the cron. Instead of atrun to scan the
|
||||
/usr/spool/at directory, at files are put into the /usr/lib/cron
|
||||
directory along with users cron files, and cron fabricates a line from
|
||||
a crontab file to run them. This is considered a major win by all who
|
||||
use it.
|
||||
|
||||
<< I don't use 'at', and my cron doesn't do anything with it. To run
|
||||
'at', I use 'atrun' the same way the current BSD cron does. My
|
||||
crontab files are in /usr/spool/cron/crontabs, in the SysV
|
||||
tradition -- not in /usr/lib/cron. This is a configuration
|
||||
parameter, of course. >>
|
||||
|
||||
There are two known restrictions:
|
||||
|
||||
1) I don't support any of the SysV security hooks. I don't have a use
|
||||
for them, and RMS didn't like the idea at all :-).
|
||||
|
||||
<< This means cron.allow and cron.deny. I plan to support them, as
|
||||
they've been quite helpful at various HPUX sites I've administered. >>
|
||||
|
||||
2) Cron expects to be able to create files with names longer than 14
|
||||
characters, which makes it hard to run on SysV. At least one person
|
||||
was working on a port, but I don't know how it's going. That might
|
||||
make for a good reason for releasing yours, right there.
|
||||
|
||||
<< If someone has SysV (with the 14-character limit), they probably
|
||||
won't want my cron, since it doesn't add much to the standard
|
||||
version (which they may have support for). My cron is not currently
|
||||
portable to non-BSD systems, since it relies on interval timers (I
|
||||
needed to sleep for intervals more granular than seconds alone would
|
||||
allow). The port would be trivial, and I will do it if a lot of
|
||||
people ask for it... >>
|
||||
|
||||
Oh, yeah - I'm going to see about getting this cron integrated into
|
||||
the next 4BSD release.
|
||||
|
||||
<< How does one go about this? I have a few nifty gadgets I'd like
|
||||
to contribute, this cron being one of them... >>
|
||||
|
||||
<<[more FSF/GNU discussion deleted]>>
|
||||
|
||||
From qantel!hplabs!ames!ut-sally!ut-ngp!melpad!bigtex!james Tue Jan 6 21:24:57 1987
|
||||
Posted-Date: Fri, 2 Jan 87 19:26:16 est
|
||||
Date: Fri, 2 Jan 87 19:26:16 est
|
||||
From: hplabs!ames!ut-sally!ut-ngp!bigtex!james
|
||||
To: vixie!paul
|
||||
Status: RO
|
||||
|
||||
Yes!!! There are several critical failures in System V cron...
|
||||
|
||||
1. Pass all variables in cron's environment into the environment of things
|
||||
cron starts up, or at least into the crontab entries started up (at jobs
|
||||
will inherit the environment of the user). If nothing else it is critically
|
||||
important that the TZ variable be passed on. PATH should be passed on too.
|
||||
Basically, passing environment values allows one to design a standard
|
||||
environment with TZ and PATH and have that run by everything. If anyone
|
||||
tells you this is no big deal, consider what happens when uucico is
|
||||
started by cron in CA to make a long distance phone link... Unless the
|
||||
administrator is really on his/her toes, calls scheduled at 5pm will really
|
||||
go at two in the afternoon, needlessly incurring huge phone bills, all
|
||||
because System V refuses to pass the TZ from its environment down. There
|
||||
are work arounds, but only putting it in cron will really work. This is
|
||||
not a security hole.
|
||||
|
||||
<< delete TERM and TERMCAP; modify HOME, USER, and CWD; pass TZ and
|
||||
PATH through undisturbed. any other requests out there?
|
||||
|
||||
BSD doesn't have this problem -- TZ is passed right on through if
|
||||
you define it in the shell before starting my cron daemon. However,
|
||||
the BSD I'm running this on doesn't need TZ to be defined anyway...
|
||||
The default in the kernel has been just fine so far... But just the
|
||||
same, if/when I port to SysV (I guess I really should), I'll make
|
||||
sure this works right.
|
||||
|
||||
I guess I've been spoiled. HPUX is SysV-based, and I never had a
|
||||
problem with cron and TZ when I used it. >>
|
||||
|
||||
2. A way to avoid logging stuff in /usr/lib/cron/log. I have a cron entry
|
||||
run uudemon.hr every 10 minutes. This is 144 times/day. Each run generates
|
||||
three lines of text, for a total of 432 lines of text I don't want to see.
|
||||
Obviously this should be optional, but it would be nice if there were a
|
||||
way to flag an entry so that it wasn't logged at all unless there was an
|
||||
error.
|
||||
|
||||
<< I don't know nothin' 'bout no /usr/lib/cron/log. What is this file?
|
||||
I don't see any reason to create log entries, given the mail-the-
|
||||
output behaviour. Opinions, anyone? >>
|
||||
|
||||
I will come up with other ideas no doubt, but I can always implement them
|
||||
myself.
|
||||
|
||||
<< That's what I like about PD software. Please send me the diffs! >>
|
||||
|
||||
The other problem you have is making sure you can run standard
|
||||
crontabs. I would suggest something like this: if the command part of the
|
||||
entry starts with an unescaped -, then flags and options follow immediately
|
||||
thereafter. As in:
|
||||
|
||||
2,12,22,32,42,52 * * * * -q /usr/lib/uucp/uudemon.hr
|
||||
|
||||
This could mean do not log the uudemon.hr run unless there is a problem of
|
||||
some kind. This is probably safe as not many filenames start with "-", and
|
||||
those that do are already a problem for people.
|
||||
|
||||
<< Since I don't plan on supporting /usr/lib/cron/log in ANY form unless
|
||||
many people request it, I won't be needing -q as you've defined it.
|
||||
I could use something like this to avoid sending mail on errors, for
|
||||
the occasional script that you don't want to bullet-proof.
|
||||
|
||||
The compatibility issue is CRITICAL. The 4.3BSD crontab format is
|
||||
a crime against the whole philosophy of Unix(TM), in my opinion. >>
|
||||
|
||||
One other minor thing to consider is the ulimit: can different users get
|
||||
different ulimits for their crontab entries?
|
||||
|
||||
<< Boy I'm ignorant today. What's a ulimit, and what should I do with
|
||||
it in a crontab? Suggestions, enlightenment, etc ?? >>
|
||||
|
||||
From qantel!lll-crg!ames!uw-beaver!uw-nsr!john Tue Jan 6 23:32:44 1987
|
||||
Date: Thu, 1 Jan 87 10:53:05 pst
|
||||
From: lll-crg!ames!uw-beaver!uw-nsr!john (John Sambrook 5-7433)
|
||||
To: vixie!paul
|
||||
Status: RO
|
||||
|
||||
How about not hardwiring the default environment that cron builds for its
|
||||
children in the cron program itself? Our cron does this and it's the pits
|
||||
because we are TZ=PST8PDT not TZ=EST5EDT !
|
||||
|
||||
<< yeachk. I assure you, I will not hardwire the TZ! >>
|
||||
From ptsfa!well!dv Fri Jan 9 04:01:50 1987
|
||||
Date: Thu, 8 Jan 87 23:50:40 pst
|
||||
From: well!dv (David W. Vezie)
|
||||
To: ptsfa!vixie!paul
|
||||
Status: RO
|
||||
|
||||
6, have a special notation called 'H' which would expand to weekends
|
||||
and holidays (you'd have to keep a database somewhere of real
|
||||
holidays), and also 'W' for workdays (neither weekend or holiday).
|
||||
|
||||
<< Too much work. There should be a standard way to define and
|
||||
detect holidays under Unix(TM); if there were, I'd use it. As
|
||||
it is, I'll leave this for someone else to add.
|
||||
|
||||
I can see the usefulness; it just doesn't quite seem worth it. >>
|
||||
From qantel!gatech!akgua!blnt1!jat Wed Jan 14 20:00:40 1987
|
||||
Date: Tue, 13 Jan 87 16:39:38 EST
|
||||
From: gatech!akgua!blnt1!jat
|
||||
Status: RO
|
||||
|
||||
1) Add some way to tell cron to reread the files, say kill -1 <pid>
|
||||
|
||||
<< whenever the 'crontab' program is run and updates a crontab file,
|
||||
a file /usr/spool/cron/POKECRON is created; next time the cron
|
||||
daemon wakes up, it sees it, and re-reads the crontab files.
|
||||
|
||||
I thought of handling the signal; even implemented it. Then this
|
||||
clever idea hit me and I ripped it all out and added a single
|
||||
IF-statement to handle the POKECRON file. >>
|
||||
|
||||
2) Have some kind of retry time so that if a command fails, cron will try to
|
||||
execute it again after a certain period. This is useful if you have some
|
||||
type of cleanup program that can run at the scheduled time for some reason
|
||||
(such as locked device, unmounted filesystem, etc).
|
||||
|
||||
<< Hmmm, sounds useful. I could do this by submitting an 'at' job...
|
||||
I'll think about it. >>
|
||||
From ptsfa!dual!ucbvax!ihnp4!mtuxo!ender Sat Jan 3 16:54:00 1987
|
||||
From: dual!ucbvax!ihnp4!mtuxo!ender
|
||||
Date: Sat, 3 Jan 87 14:05:13 PST
|
||||
To: ucbvax!dual!ptsfa!vixie!paul
|
||||
Status: RO
|
||||
|
||||
It would be nice if nonprivileged users can setup personal crontab files
|
||||
(~/.cronrc, say) and be able to run personal jobs at regular intervals.
|
||||
|
||||
<< this is done, but in the SysV style: the 'crontab' program installs
|
||||
a new crontab file for the executing user (can be overridden by root
|
||||
for setup of uucp and news). the advantage of this is that (1) when
|
||||
a crontab is changed, the daemon can be informed automatically; and
|
||||
(2) the file can be syntax-checked before installation. >>
|
||||
From ptsfa!ames!seismo!ihnp4!lcc!richard Fri Jan 16 04:47:33 1987
|
||||
Date: Fri, 16 Jan 87 07:44:57 EST
|
||||
To: nike!ptsfa!vixie!paul
|
||||
Status: RO
|
||||
|
||||
The System V cron is nice, but it has a few annoying features. One is that
|
||||
its mail files will say that the previous message is the output of "one of your
|
||||
cron commands." I wish it would say WHICH cron commmand.
|
||||
|
||||
<< Done. Also which shell, which user (useful when the mail gets
|
||||
forwarded), which home directory, and other useful crud. >>
|
||||
|
||||
Another problem is with timezones. It is necessary to specify TZ=PST8PDT (or
|
||||
whatever) when you invoke cron (from inittab, or /etc/rc) and it is also
|
||||
necessary to add TZ=PST8PDT to each crontab line which might need it. Cron
|
||||
should automatically export its idea of the "TZ" to each invoked command, and
|
||||
it should be possible to put a line in the crontab file which overrides that
|
||||
for every command in the file (e.g., most users are on EST, so cron is run
|
||||
with TZ=EST5EDT; but one user is usually on PST and wants all of his cron
|
||||
commands to run with TZ=PST8PDT). This might be extended to allow any
|
||||
environment variable to be specified once for the whole crontab file (e.g.,
|
||||
PATH).
|
||||
|
||||
<< Well, since I run the user's shell, you could put this into .cshrc.
|
||||
generic environment-variable setting could be useful, though. Since
|
||||
I have to modify the environment anyway, I'll consider this. >>
|
||||
|
||||
A log file might be a nice idea, but the System V cron log is too verbose.
|
||||
I seem to remember that cron keeps it open, too; so you can't even have
|
||||
something go and periodically clean it out.
|
||||
|
||||
<< I don't do /usr/lib/cron/log. I wasn't aware of this file until I
|
||||
got all these suggestions. Do people want this file? Tell me! >>
|
||||
33
src/cmd/cron/grot/MANIFEST
Executable file
33
src/cmd/cron/grot/MANIFEST
Executable file
@@ -0,0 +1,33 @@
|
||||
File Name Archive # Description
|
||||
-----------------------------------------------------------
|
||||
CHANGES 2
|
||||
CONVERSION 1
|
||||
FEATURES 1
|
||||
INSTALL 1
|
||||
MAIL 2
|
||||
MANIFEST 1 This shipping list
|
||||
Makefile 1
|
||||
README 1
|
||||
THANKS 1
|
||||
bitstring.3 1
|
||||
bitstring.h 1
|
||||
compat.c 1
|
||||
compat.h 1
|
||||
config.h 1
|
||||
cron.8 1
|
||||
cron.c 1
|
||||
cron.h 1
|
||||
crontab.1 1
|
||||
crontab.5 1
|
||||
crontab.c 2
|
||||
database.c 1
|
||||
do_command.c 2
|
||||
entry.c 2
|
||||
env.c 1
|
||||
externs.h 1
|
||||
job.c 1
|
||||
misc.c 2
|
||||
pathnames.h 1
|
||||
popen.c 1
|
||||
putman.sh 1
|
||||
user.c 1
|
||||
29
src/cmd/cron/grot/THANKS
Executable file
29
src/cmd/cron/grot/THANKS
Executable file
@@ -0,0 +1,29 @@
|
||||
15 January 1990
|
||||
Paul Vixie
|
||||
|
||||
Many people have contributed to cron. Many more than I can remember, in fact.
|
||||
Rich Salz and Carl Gutekunst were each of enormous help to me in V1; Carl for
|
||||
helping me understand UNIX well enough to write it, and Rich for helping me
|
||||
get the features right.
|
||||
|
||||
John Gilmore wrote me a wonderful review of V2, which took me a whole year to
|
||||
answer even though it made me clean up some really awful things in the code.
|
||||
(According to John the most awful things are still in here, of course.)
|
||||
|
||||
Paul Close made a suggestion which led to /etc/crond.pid and the mutex locking
|
||||
on it. Kevin Braunsdorf of Purdue made a suggestion that led to @reboot and
|
||||
its brothers and sisters; he also sent some diffs that lead cron toward compil-
|
||||
ability with System V, though without at(1) capabilities, this cron isn't going
|
||||
to be that useful on System V. Bob Alverson fixed a silly bug in the line
|
||||
number counting. Brian Reid made suggestions which led to the run queue and
|
||||
the source-file labelling in installed crontabs.
|
||||
|
||||
Scott Narveson ported V2 to a Sequent, and sent in the most useful single batch
|
||||
of diffs I got from anybody. Changes attributable to Scott are:
|
||||
-> sendmail won't time out if the command is slow to generate output
|
||||
-> day-of-week names aren't off by one anymore
|
||||
-> crontab says the right thing if you do something you shouldn't do
|
||||
-> crontab(5) man page is longer and more informative
|
||||
-> misc changes related to the side effects of fclose()
|
||||
-> Sequent "universe" support added (may also help on Pyramids)
|
||||
-> null pw_shell is dealt with now; default is /bin/sh
|
||||
1933
src/cmd/cron/grot/diffs
Executable file
1933
src/cmd/cron/grot/diffs
Executable file
File diff suppressed because it is too large
Load Diff
74
src/cmd/cron/job.c
Executable file
74
src/cmd/cron/job.c
Executable file
@@ -0,0 +1,74 @@
|
||||
/* Copyright 1988,1990,1993,1994 by Paul Vixie
|
||||
* All rights reserved
|
||||
*
|
||||
* Distribute freely, except: don't remove my name from the source or
|
||||
* documentation (don't take credit for my work), mark your changes (don't
|
||||
* get me blamed for your possible bugs), don't alter or remove this
|
||||
* notice. May be sold if buildable source is provided to buyer. No
|
||||
* warrantee of any kind, express or implied, is included with this
|
||||
* software; use at your own risk, responsibility for damages (if any) to
|
||||
* anyone resulting from the use of this software rests entirely with the
|
||||
* user.
|
||||
*
|
||||
* Send bug reports, bug fixes, enhancements, requests, flames, etc., and
|
||||
* I'll try to keep a version up to date. I can be reached as follows:
|
||||
* Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
|
||||
*/
|
||||
|
||||
#if !defined(lint) && !defined(LINT)
|
||||
static char rcsid[] = "$Id: job.c,v 1.6 1994/01/15 20:43:43 vixie Exp $";
|
||||
#endif
|
||||
|
||||
|
||||
#include "cron.h"
|
||||
|
||||
|
||||
typedef struct _job {
|
||||
struct _job *next;
|
||||
entry *e;
|
||||
user *u;
|
||||
} job;
|
||||
|
||||
|
||||
static job *jhead = NULL, *jtail = NULL;
|
||||
|
||||
|
||||
void
|
||||
job_add(e, u)
|
||||
register entry *e;
|
||||
register user *u;
|
||||
{
|
||||
register job *j;
|
||||
|
||||
/* if already on queue, keep going */
|
||||
for (j=jhead; j; j=j->next)
|
||||
if (j->e == e && j->u == u) { return; }
|
||||
|
||||
/* build a job queue element */
|
||||
j = (job*)malloc(sizeof(job));
|
||||
j->next = (job*) NULL;
|
||||
j->e = e;
|
||||
j->u = u;
|
||||
|
||||
/* add it to the tail */
|
||||
if (!jhead) { jhead=j; }
|
||||
else { jtail->next=j; }
|
||||
jtail = j;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
job_runqueue()
|
||||
{
|
||||
register job *j, *jn;
|
||||
register int run = 0;
|
||||
|
||||
for (j=jhead; j; j=jn) {
|
||||
do_command(j->e, j->u);
|
||||
jn = j->next;
|
||||
free(j);
|
||||
run++;
|
||||
}
|
||||
jhead = jtail = NULL;
|
||||
return run;
|
||||
}
|
||||
649
src/cmd/cron/misc.c
Executable file
649
src/cmd/cron/misc.c
Executable file
@@ -0,0 +1,649 @@
|
||||
/* Copyright 1988,1990,1993,1994 by Paul Vixie
|
||||
* All rights reserved
|
||||
*
|
||||
* Distribute freely, except: don't remove my name from the source or
|
||||
* documentation (don't take credit for my work), mark your changes (don't
|
||||
* get me blamed for your possible bugs), don't alter or remove this
|
||||
* notice. May be sold if buildable source is provided to buyer. No
|
||||
* warrantee of any kind, express or implied, is included with this
|
||||
* software; use at your own risk, responsibility for damages (if any) to
|
||||
* anyone resulting from the use of this software rests entirely with the
|
||||
* user.
|
||||
*
|
||||
* Send bug reports, bug fixes, enhancements, requests, flames, etc., and
|
||||
* I'll try to keep a version up to date. I can be reached as follows:
|
||||
* Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
|
||||
*/
|
||||
|
||||
#if !defined(lint) && !defined(LINT)
|
||||
static char rcsid[] = "$Id: misc.c,v 2.9 1994/01/15 20:43:43 vixie Exp $";
|
||||
#endif
|
||||
|
||||
/* vix 26jan87 [RCS has the rest of the log]
|
||||
* vix 30dec86 [written]
|
||||
*/
|
||||
|
||||
|
||||
#include "cron.h"
|
||||
#include <sys/time.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#if defined(SYSLOG)
|
||||
# include <syslog.h>
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(LOG_DAEMON) && !defined(LOG_CRON)
|
||||
#define LOG_CRON LOG_DAEMON
|
||||
#endif
|
||||
|
||||
static int LogFD = ERR;
|
||||
|
||||
int
|
||||
strcmp_until(left, right, until)
|
||||
register char *left;
|
||||
register char *right;
|
||||
int until;
|
||||
{
|
||||
register int diff;
|
||||
|
||||
while (*left && *left != until && *left == *right) {
|
||||
left++;
|
||||
right++;
|
||||
}
|
||||
|
||||
if ((*left=='\0' || *left == until) &&
|
||||
(*right=='\0' || *right == until)) {
|
||||
diff = 0;
|
||||
} else {
|
||||
diff = *left - *right;
|
||||
}
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
|
||||
/* strdtb(s) - delete trailing blanks in string 's' and return new length
|
||||
*/
|
||||
int
|
||||
strdtb(s)
|
||||
register char *s;
|
||||
{
|
||||
register char *x = s;
|
||||
|
||||
/* scan forward to the null
|
||||
*/
|
||||
while (*x)
|
||||
x++;
|
||||
|
||||
/* scan backward to either the first character before the string,
|
||||
* or the last non-blank in the string, whichever comes first.
|
||||
*/
|
||||
do {x--;}
|
||||
while (x >= s && isspace(*x));
|
||||
|
||||
/* one character beyond where we stopped above is where the null
|
||||
* goes.
|
||||
*/
|
||||
*++x = '\0';
|
||||
|
||||
/* the difference between the position of the null character and
|
||||
* the position of the first character of the string is the length.
|
||||
*/
|
||||
return x - s;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
set_debug_flags(flags)
|
||||
char *flags;
|
||||
{
|
||||
/* debug flags are of the form flag[,flag ...]
|
||||
*
|
||||
* if an error occurs, print a message to stdout and return FALSE.
|
||||
* otherwise return TRUE after setting ERROR_FLAGS.
|
||||
*/
|
||||
|
||||
#if !DEBUGGING
|
||||
|
||||
printf("this program was compiled without debugging enabled\n");
|
||||
return FALSE;
|
||||
|
||||
#else /* DEBUGGING */
|
||||
|
||||
register char *pc = flags;
|
||||
|
||||
DebugFlags = 0;
|
||||
|
||||
while (*pc) {
|
||||
char **test;
|
||||
int mask;
|
||||
|
||||
/* try to find debug flag name in our list.
|
||||
*/
|
||||
for ( test = DebugFlagNames, mask = 1;
|
||||
*test && strcmp_until(*test, pc, ',');
|
||||
test++, mask <<= 1
|
||||
)
|
||||
;
|
||||
|
||||
if (!*test) {
|
||||
fprintf(stderr,
|
||||
"unrecognized debug flag <%s> <%s>\n",
|
||||
flags, pc);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DebugFlags |= mask;
|
||||
|
||||
/* skip to the next flag
|
||||
*/
|
||||
while (*pc && *pc != ',')
|
||||
pc++;
|
||||
if (*pc == ',')
|
||||
pc++;
|
||||
}
|
||||
|
||||
if (DebugFlags) {
|
||||
int flag;
|
||||
|
||||
fprintf(stderr, "debug flags enabled:");
|
||||
|
||||
for (flag = 0; DebugFlagNames[flag]; flag++)
|
||||
if (DebugFlags & (1 << flag))
|
||||
fprintf(stderr, " %s", DebugFlagNames[flag]);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
#endif /* DEBUGGING */
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
set_cron_uid()
|
||||
{
|
||||
if (seteuid(ROOT_UID) < OK) {
|
||||
perror("seteuid");
|
||||
exit(ERROR_EXIT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
set_cron_cwd()
|
||||
{
|
||||
struct stat sb;
|
||||
|
||||
/* first check for CRONDIR ("/var/cron" or some such)
|
||||
*/
|
||||
if (stat(CRONDIR, &sb) < OK && errno == ENOENT) {
|
||||
perror(CRONDIR);
|
||||
if (OK == mkdir(CRONDIR, 0700)) {
|
||||
fprintf(stderr, "%s: created\n", CRONDIR);
|
||||
stat(CRONDIR, &sb);
|
||||
} else {
|
||||
fprintf(stderr, "%s: ", CRONDIR);
|
||||
perror("mkdir");
|
||||
exit(ERROR_EXIT);
|
||||
}
|
||||
}
|
||||
if (!(sb.st_mode & S_IFDIR)) {
|
||||
fprintf(stderr, "'%s' is not a directory, bailing out.\n",
|
||||
CRONDIR);
|
||||
exit(ERROR_EXIT);
|
||||
}
|
||||
if (chdir(CRONDIR) < OK) {
|
||||
fprintf(stderr, "cannot chdir(%s), bailing out.\n", CRONDIR);
|
||||
perror(CRONDIR);
|
||||
exit(ERROR_EXIT);
|
||||
}
|
||||
|
||||
/* CRONDIR okay (now==CWD), now look at SPOOL_DIR ("tabs" or some such)
|
||||
*/
|
||||
if (stat(SPOOL_DIR, &sb) < OK && errno == ENOENT) {
|
||||
perror(SPOOL_DIR);
|
||||
if (OK == mkdir(SPOOL_DIR, 0700)) {
|
||||
fprintf(stderr, "%s: created\n", SPOOL_DIR);
|
||||
stat(SPOOL_DIR, &sb);
|
||||
} else {
|
||||
fprintf(stderr, "%s: ", SPOOL_DIR);
|
||||
perror("mkdir");
|
||||
exit(ERROR_EXIT);
|
||||
}
|
||||
}
|
||||
if (!(sb.st_mode & S_IFDIR)) {
|
||||
fprintf(stderr, "'%s' is not a directory, bailing out.\n",
|
||||
SPOOL_DIR);
|
||||
exit(ERROR_EXIT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* acquire_daemonlock() - write our PID into /etc/cron.pid, unless
|
||||
* another daemon is already running, which we detect here.
|
||||
*
|
||||
* note: main() calls us twice; once before forking, once after.
|
||||
* we maintain static storage of the file pointer so that we
|
||||
* can rewrite our PID into the PIDFILE after the fork.
|
||||
*
|
||||
* it would be great if fflush() disassociated the file buffer.
|
||||
*/
|
||||
void
|
||||
acquire_daemonlock(closeflag)
|
||||
int closeflag;
|
||||
{
|
||||
static FILE *fp = NULL;
|
||||
|
||||
if (closeflag && fp) {
|
||||
fclose(fp);
|
||||
fp = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fp) {
|
||||
char pidfile[MAX_FNAME];
|
||||
char buf[MAX_TEMPSTR];
|
||||
int fd, otherpid;
|
||||
|
||||
(void) sprintf(pidfile, PIDFILE, PIDDIR);
|
||||
if ((-1 == (fd = open(pidfile, O_RDWR|O_CREAT, 0644)))
|
||||
|| (NULL == (fp = fdopen(fd, "r+")))
|
||||
) {
|
||||
sprintf(buf, "can't open or create %s: %s",
|
||||
pidfile, strerror(errno));
|
||||
fprintf(stderr, "%s: %s\n", ProgramName, buf);
|
||||
log_it("CRON", getpid(), "DEATH", buf);
|
||||
exit(ERROR_EXIT);
|
||||
}
|
||||
|
||||
if (flock(fd, LOCK_EX|LOCK_NB) < OK) {
|
||||
int save_errno = errno;
|
||||
|
||||
fscanf(fp, "%d", &otherpid);
|
||||
sprintf(buf, "can't lock %s, otherpid may be %d: %s",
|
||||
pidfile, otherpid, strerror(save_errno));
|
||||
fprintf(stderr, "%s: %s\n", ProgramName, buf);
|
||||
log_it("CRON", getpid(), "DEATH", buf);
|
||||
exit(ERROR_EXIT);
|
||||
}
|
||||
|
||||
(void) fcntl(fd, F_SETFD, 1);
|
||||
}
|
||||
|
||||
rewind(fp);
|
||||
fprintf(fp, "%d\n", getpid());
|
||||
fflush(fp);
|
||||
(void) ftruncate(fileno(fp), ftell(fp));
|
||||
|
||||
/* abandon fd and fp even though the file is open. we need to
|
||||
* keep it open and locked, but we don't need the handles elsewhere.
|
||||
*/
|
||||
}
|
||||
|
||||
/* get_char(file) : like getc() but increment LineNumber on newlines
|
||||
*/
|
||||
int
|
||||
get_char(file)
|
||||
register FILE *file;
|
||||
{
|
||||
register int ch;
|
||||
|
||||
ch = getc(file);
|
||||
if (ch == '\n')
|
||||
Set_LineNum(LineNumber + 1)
|
||||
return ch;
|
||||
}
|
||||
|
||||
|
||||
/* unget_char(ch, file) : like ungetc but do LineNumber processing
|
||||
*/
|
||||
void
|
||||
unget_char(ch, file)
|
||||
register int ch;
|
||||
register FILE *file;
|
||||
{
|
||||
ungetc(ch, file);
|
||||
if (ch == '\n')
|
||||
Set_LineNum(LineNumber - 1)
|
||||
}
|
||||
|
||||
|
||||
/* get_string(str, max, file, termstr) : like fgets() but
|
||||
* (1) has terminator string which should include \n
|
||||
* (2) will always leave room for the null
|
||||
* (3) uses get_char() so LineNumber will be accurate
|
||||
* (4) returns EOF or terminating character, whichever
|
||||
*/
|
||||
int
|
||||
get_string(string, size, file, terms)
|
||||
register char *string;
|
||||
int size;
|
||||
FILE *file;
|
||||
char *terms;
|
||||
{
|
||||
register int ch;
|
||||
|
||||
while (EOF != (ch = get_char(file)) && !strchr(terms, ch)) {
|
||||
if (size > 1) {
|
||||
*string++ = (char) ch;
|
||||
size--;
|
||||
}
|
||||
}
|
||||
|
||||
if (size > 0)
|
||||
*string = '\0';
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
|
||||
/* skip_comments(file) : read past comment (if any)
|
||||
*/
|
||||
void
|
||||
skip_comments(file)
|
||||
register FILE *file;
|
||||
{
|
||||
register int ch;
|
||||
|
||||
while (EOF != (ch = get_char(file))) {
|
||||
/* ch is now the first character of a line.
|
||||
*/
|
||||
|
||||
while (ch == ' ' || ch == '\t')
|
||||
ch = get_char(file);
|
||||
|
||||
if (ch == EOF)
|
||||
break;
|
||||
|
||||
/* ch is now the first non-blank character of a line.
|
||||
*/
|
||||
|
||||
if (ch != '\n' && ch != '#')
|
||||
break;
|
||||
|
||||
/* ch must be a newline or comment as first non-blank
|
||||
* character on a line.
|
||||
*/
|
||||
|
||||
while (ch != '\n' && ch != EOF)
|
||||
ch = get_char(file);
|
||||
|
||||
/* ch is now the newline of a line which we're going to
|
||||
* ignore.
|
||||
*/
|
||||
}
|
||||
if (ch != EOF)
|
||||
unget_char(ch, file);
|
||||
}
|
||||
|
||||
|
||||
/* int in_file(char *string, FILE *file)
|
||||
* return TRUE if one of the lines in file matches string exactly,
|
||||
* FALSE otherwise.
|
||||
*/
|
||||
static int
|
||||
in_file(string, file)
|
||||
char *string;
|
||||
FILE *file;
|
||||
{
|
||||
char line[MAX_TEMPSTR];
|
||||
|
||||
rewind(file);
|
||||
while (fgets(line, MAX_TEMPSTR, file)) {
|
||||
if (line[0] != '\0')
|
||||
line[strlen(line)-1] = '\0';
|
||||
if (0 == strcmp(line, string))
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/* int allowed(char *username)
|
||||
* returns TRUE if (ALLOW_FILE exists and user is listed)
|
||||
* or (DENY_FILE exists and user is NOT listed)
|
||||
* or (neither file exists but user=="root" so it's okay)
|
||||
*/
|
||||
int
|
||||
allowed(username)
|
||||
char *username;
|
||||
{
|
||||
static int init = FALSE;
|
||||
static FILE *allow, *deny;
|
||||
|
||||
if (!init) {
|
||||
init = TRUE;
|
||||
#if defined(ALLOW_FILE) && defined(DENY_FILE)
|
||||
allow = fopen(ALLOW_FILE, "r");
|
||||
deny = fopen(DENY_FILE, "r");
|
||||
Debug(DMISC, ("allow/deny enabled, %d/%d\n", !!allow, !!deny))
|
||||
#else
|
||||
allow = NULL;
|
||||
deny = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (allow)
|
||||
return (in_file(username, allow));
|
||||
if (deny)
|
||||
return (!in_file(username, deny));
|
||||
|
||||
#if defined(ALLOW_ONLY_ROOT)
|
||||
return (strcmp(username, ROOT_USER) == 0);
|
||||
#else
|
||||
return TRUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
log_it(username, xpid, event, detail)
|
||||
char *username;
|
||||
int xpid;
|
||||
char *event;
|
||||
char *detail;
|
||||
{
|
||||
PID_T pid = xpid;
|
||||
#if defined(LOG_FILE)
|
||||
char *msg;
|
||||
TIME_T now = time((TIME_T) 0);
|
||||
register struct tm *t = localtime(&now);
|
||||
#endif /*LOG_FILE*/
|
||||
|
||||
#if defined(SYSLOG)
|
||||
static int syslog_open = 0;
|
||||
#endif
|
||||
|
||||
#if defined(LOG_FILE)
|
||||
/* we assume that MAX_TEMPSTR will hold the date, time, &punctuation.
|
||||
*/
|
||||
msg = (char *)malloc(strlen(username)
|
||||
+ strlen(event)
|
||||
+ strlen(detail)
|
||||
+ MAX_TEMPSTR);
|
||||
|
||||
if (LogFD < OK) {
|
||||
LogFD = open(LOG_FILE, O_WRONLY|O_APPEND|O_CREAT, 0600);
|
||||
if (LogFD < OK) {
|
||||
fprintf(stderr, "%s: can't open log file\n",
|
||||
ProgramName);
|
||||
perror(LOG_FILE);
|
||||
} else {
|
||||
(void) fcntl(LogFD, F_SETFD, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* we have to sprintf() it because fprintf() doesn't always write
|
||||
* everything out in one chunk and this has to be atomically appended
|
||||
* to the log file.
|
||||
*/
|
||||
sprintf(msg, "%s (%02d/%02d-%02d:%02d:%02d-%d) %s (%s)\n",
|
||||
username,
|
||||
t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, pid,
|
||||
event, detail);
|
||||
|
||||
/* we have to run strlen() because sprintf() returns (char*) on old BSD
|
||||
*/
|
||||
if (LogFD < OK || write(LogFD, msg, strlen(msg)) < OK) {
|
||||
if (LogFD >= OK)
|
||||
perror(LOG_FILE);
|
||||
fprintf(stderr, "%s: can't write to log file\n", ProgramName);
|
||||
write(STDERR, msg, strlen(msg));
|
||||
}
|
||||
|
||||
free(msg);
|
||||
#endif /*LOG_FILE*/
|
||||
|
||||
#if defined(SYSLOG)
|
||||
if (!syslog_open) {
|
||||
/* we don't use LOG_PID since the pid passed to us by
|
||||
* our client may not be our own. therefore we want to
|
||||
* print the pid ourselves.
|
||||
*/
|
||||
openlog(ProgramName, LOG_PID, LOG_CRON);
|
||||
syslog_open = TRUE; /* assume openlog success */
|
||||
}
|
||||
|
||||
syslog(LOG_INFO, "(%s) %s (%s)\n", username, event, detail);
|
||||
|
||||
#endif /*SYSLOG*/
|
||||
|
||||
#if DEBUGGING
|
||||
if (DebugFlags) {
|
||||
fprintf(stderr, "log_it: (%s %d) %s (%s)\n",
|
||||
username, pid, event, detail);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
log_close() {
|
||||
if (LogFD != ERR) {
|
||||
close(LogFD);
|
||||
LogFD = ERR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* two warnings:
|
||||
* (1) this routine is fairly slow
|
||||
* (2) it returns a pointer to static storage
|
||||
*/
|
||||
char *
|
||||
first_word(s, t)
|
||||
register char *s; /* string we want the first word of */
|
||||
char *t; /* terminators, implicitly including \0 */
|
||||
{
|
||||
static char retbuf[2][MAX_TEMPSTR + 1]; /* sure wish C had GC */
|
||||
static int retsel = 0;
|
||||
register char *rb, *rp;
|
||||
|
||||
/* select a return buffer */
|
||||
retsel = 1-retsel;
|
||||
rb = &retbuf[retsel][0];
|
||||
rp = rb;
|
||||
|
||||
/* skip any leading terminators */
|
||||
while (*s && (NULL != strchr(t, *s))) {
|
||||
s++;
|
||||
}
|
||||
|
||||
/* copy until next terminator or full buffer */
|
||||
while (*s && (NULL == strchr(t, *s)) && (rp < &rb[MAX_TEMPSTR])) {
|
||||
*rp++ = *s++;
|
||||
}
|
||||
|
||||
/* finish the return-string and return it */
|
||||
*rp = '\0';
|
||||
return rb;
|
||||
}
|
||||
|
||||
|
||||
/* warning:
|
||||
* heavily ascii-dependent.
|
||||
*/
|
||||
void
|
||||
mkprint(dst, src, len)
|
||||
register char *dst;
|
||||
register unsigned char *src;
|
||||
register int len;
|
||||
{
|
||||
while (len-- > 0)
|
||||
{
|
||||
register unsigned char ch = *src++;
|
||||
|
||||
if (ch < ' ') { /* control character */
|
||||
*dst++ = '^';
|
||||
*dst++ = ch + '@';
|
||||
} else if (ch < 0177) { /* printable */
|
||||
*dst++ = ch;
|
||||
} else if (ch == 0177) { /* delete/rubout */
|
||||
*dst++ = '^';
|
||||
*dst++ = '?';
|
||||
} else { /* parity character */
|
||||
sprintf(dst, "\\%03o", ch);
|
||||
dst += 4;
|
||||
}
|
||||
}
|
||||
*dst = '\0';
|
||||
}
|
||||
|
||||
|
||||
/* warning:
|
||||
* returns a pointer to malloc'd storage, you must call free yourself.
|
||||
*/
|
||||
char *
|
||||
mkprints(src, len)
|
||||
unsigned char *src;
|
||||
unsigned int len;
|
||||
{
|
||||
register char *dst = (char *)malloc(len*4 + 1);
|
||||
|
||||
mkprint(dst, src, len);
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
#ifdef MAIL_DATE
|
||||
/* Sat, 27 Feb 93 11:44:51 CST
|
||||
* 123456789012345678901234567
|
||||
*/
|
||||
char *
|
||||
arpadate(clock)
|
||||
time_t *clock;
|
||||
{
|
||||
time_t t = clock ?*clock :time(0L);
|
||||
struct tm *tm = localtime(&t);
|
||||
static char ret[30]; /* zone name might be >3 chars */
|
||||
|
||||
(void) sprintf(ret, "%s, %2d %s %2d %02d:%02d:%02d %s",
|
||||
DowNames[tm->tm_wday],
|
||||
tm->tm_mday,
|
||||
MonthNames[tm->tm_mon],
|
||||
tm->tm_year,
|
||||
tm->tm_hour,
|
||||
tm->tm_min,
|
||||
tm->tm_sec,
|
||||
TZONE(*tm));
|
||||
return ret;
|
||||
}
|
||||
#endif /*MAIL_DATE*/
|
||||
|
||||
static int save_euid;
|
||||
int swap_uids()
|
||||
{
|
||||
save_euid = geteuid();
|
||||
return(seteuid(getuid()));
|
||||
}
|
||||
|
||||
int swap_uids_back()
|
||||
{
|
||||
return(seteuid(save_euid));
|
||||
}
|
||||
79
src/cmd/cron/pathnames.h
Executable file
79
src/cmd/cron/pathnames.h
Executable file
@@ -0,0 +1,79 @@
|
||||
/* Copyright 1993,1994 by Paul Vixie
|
||||
* All rights reserved
|
||||
*
|
||||
* Distribute freely, except: don't remove my name from the source or
|
||||
* documentation (don't take credit for my work), mark your changes (don't
|
||||
* get me blamed for your possible bugs), don't alter or remove this
|
||||
* notice. May be sold if buildable source is provided to buyer. No
|
||||
* warrantee of any kind, express or implied, is included with this
|
||||
* software; use at your own risk, responsibility for damages (if any) to
|
||||
* anyone resulting from the use of this software rests entirely with the
|
||||
* user.
|
||||
*
|
||||
* Send bug reports, bug fixes, enhancements, requests, flames, etc., and
|
||||
* I'll try to keep a version up to date. I can be reached as follows:
|
||||
* Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
|
||||
*/
|
||||
|
||||
/*
|
||||
* $Id: pathnames.h,v 1.3 1994/01/15 20:43:43 vixie Exp $
|
||||
*/
|
||||
|
||||
#include <paths.h>
|
||||
|
||||
#ifndef CRONDIR
|
||||
/* CRONDIR is where crond(8) and crontab(1) both chdir
|
||||
* to; SPOOL_DIR, ALLOW_FILE, DENY_FILE, and LOG_FILE
|
||||
* are all relative to this directory.
|
||||
*/
|
||||
#define CRONDIR "/var/cron"
|
||||
#endif
|
||||
|
||||
/* SPOOLDIR is where the crontabs live.
|
||||
* This directory will have its modtime updated
|
||||
* whenever crontab(1) changes a crontab; this is
|
||||
* the signal for crond(8) to look at each individual
|
||||
* crontab file and reload those whose modtimes are
|
||||
* newer than they were last time around (or which
|
||||
* didn't exist last time around...)
|
||||
*/
|
||||
#define SPOOL_DIR "tabs"
|
||||
|
||||
/* undefining these turns off their features. note
|
||||
* that ALLOW_FILE and DENY_FILE must both be defined
|
||||
* in order to enable the allow/deny code. If neither
|
||||
* LOG_FILE or SYSLOG is defined, we don't log. If
|
||||
* both are defined, we log both ways.
|
||||
*/
|
||||
#define ALLOW_FILE "allow" /*-*/
|
||||
#define DENY_FILE "deny" /*-*/
|
||||
#undef LOG_FILE /* "log" */
|
||||
|
||||
/* where should the daemon stick its PID?
|
||||
*/
|
||||
#ifdef _PATH_VARRUN
|
||||
# define PIDDIR _PATH_VARRUN
|
||||
#else
|
||||
# define PIDDIR "/etc/"
|
||||
#endif
|
||||
#define PIDFILE "%scron.pid"
|
||||
|
||||
/* 4.3BSD-style crontab */
|
||||
#define SYSCRONTAB "/etc/crontab"
|
||||
|
||||
/* what editor to use if no EDITOR or VISUAL
|
||||
* environment variable specified.
|
||||
*/
|
||||
#if defined(_PATH_VI)
|
||||
# define EDITOR _PATH_VI
|
||||
#else
|
||||
# define EDITOR "/usr/ucb/vi"
|
||||
#endif
|
||||
|
||||
#ifndef _PATH_BSHELL
|
||||
# define _PATH_BSHELL "/bin/sh"
|
||||
#endif
|
||||
|
||||
#ifndef _PATH_DEFPATH
|
||||
# define _PATH_DEFPATH "/usr/bin:/bin"
|
||||
#endif
|
||||
170
src/cmd/cron/popen.c
Executable file
170
src/cmd/cron/popen.c
Executable file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Copyright (c) 1988 The Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software written by Ken Arnold and
|
||||
* published in UNIX Review, Vol. 6, No. 8.
|
||||
*
|
||||
* Redistribution and use in source and binary forms are permitted
|
||||
* provided that the above copyright notice and this paragraph are
|
||||
* duplicated in all such forms and that any documentation,
|
||||
* advertising materials, and other materials related to such
|
||||
* distribution and use acknowledge that the software was developed
|
||||
* by the University of California, Berkeley. The name of the
|
||||
* University may not be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
/* this came out of the ftpd sources; it's been modified to avoid the
|
||||
* globbing stuff since we don't need it. also execvp instead of execv.
|
||||
*/
|
||||
|
||||
#if !defined(lint) && defined(DOSCCS)
|
||||
static char sccsid[] = "@(#)popen.c 5.7.2 (2.11BSD) 1999/08/05";
|
||||
#endif
|
||||
|
||||
#include "cron.h"
|
||||
#include <errno.h>
|
||||
#include <sys/signal.h>
|
||||
|
||||
#define WANT_GLOBBING 0
|
||||
|
||||
/*
|
||||
* Special version of popen which avoids call to shell. This insures noone
|
||||
* may create a pipe to a hidden program as a side effect of a list or dir
|
||||
* command.
|
||||
*/
|
||||
static PID_T *pids;
|
||||
static int fds;
|
||||
|
||||
FILE *
|
||||
cron_popen(program, type)
|
||||
char *program, *type;
|
||||
{
|
||||
register char *cp;
|
||||
FILE *iop;
|
||||
int argc, pdes[2];
|
||||
PID_T pid;
|
||||
char *argv[100];
|
||||
#if WANT_GLOBBING
|
||||
char **pop, *vv[2];
|
||||
int gargc;
|
||||
char *gargv[1000];
|
||||
extern char **glob(), **copyblk();
|
||||
#endif
|
||||
|
||||
if (*type != 'r' && *type != 'w' || type[1])
|
||||
return(NULL);
|
||||
|
||||
if (!pids) {
|
||||
if ((fds = getdtablesize()) <= 0)
|
||||
return(NULL);
|
||||
if (!(pids = (PID_T *)malloc((u_int)(fds * sizeof(PID_T)))))
|
||||
return(NULL);
|
||||
bzero((char *)pids, fds * sizeof(PID_T));
|
||||
}
|
||||
if (pipe(pdes) < 0)
|
||||
return(NULL);
|
||||
|
||||
/* break up string into pieces */
|
||||
for (argc = 0, cp = program;; cp = NULL)
|
||||
if (!(argv[argc++] = strtok(cp, " \t\n")))
|
||||
break;
|
||||
|
||||
#if WANT_GLOBBING
|
||||
/* glob each piece */
|
||||
gargv[0] = argv[0];
|
||||
for (gargc = argc = 1; argv[argc]; argc++) {
|
||||
if (!(pop = glob(argv[argc]))) { /* globbing failed */
|
||||
vv[0] = argv[argc];
|
||||
vv[1] = NULL;
|
||||
pop = copyblk(vv);
|
||||
}
|
||||
argv[argc] = (char *)pop; /* save to free later */
|
||||
while (*pop && gargc < 1000)
|
||||
gargv[gargc++] = *pop++;
|
||||
}
|
||||
gargv[gargc] = NULL;
|
||||
#endif
|
||||
|
||||
iop = NULL;
|
||||
switch(pid = vfork()) {
|
||||
case -1: /* error */
|
||||
(void)close(pdes[0]);
|
||||
(void)close(pdes[1]);
|
||||
goto pfree;
|
||||
/* NOTREACHED */
|
||||
case 0: /* child */
|
||||
if (*type == 'r') {
|
||||
if (pdes[1] != 1) {
|
||||
dup2(pdes[1], 1);
|
||||
dup2(pdes[1], 2); /* stderr, too! */
|
||||
(void)close(pdes[1]);
|
||||
}
|
||||
(void)close(pdes[0]);
|
||||
} else {
|
||||
if (pdes[0] != 0) {
|
||||
dup2(pdes[0], 0);
|
||||
(void)close(pdes[0]);
|
||||
}
|
||||
(void)close(pdes[1]);
|
||||
}
|
||||
#if WANT_GLOBBING
|
||||
execvp(gargv[0], gargv);
|
||||
#else
|
||||
execvp(argv[0], argv);
|
||||
#endif
|
||||
_exit(1);
|
||||
}
|
||||
/* parent; assume fdopen can't fail... */
|
||||
if (*type == 'r') {
|
||||
iop = fdopen(pdes[0], type);
|
||||
(void)close(pdes[1]);
|
||||
} else {
|
||||
iop = fdopen(pdes[1], type);
|
||||
(void)close(pdes[0]);
|
||||
}
|
||||
pids[fileno(iop)] = pid;
|
||||
|
||||
pfree:
|
||||
#if WANT_GLOBBING
|
||||
for (argc = 1; argv[argc] != NULL; argc++) {
|
||||
/* blkfree((char **)argv[argc]); */
|
||||
free((char *)argv[argc]);
|
||||
}
|
||||
#endif
|
||||
return(iop);
|
||||
}
|
||||
|
||||
int
|
||||
cron_pclose(iop)
|
||||
FILE *iop;
|
||||
{
|
||||
register int fdes;
|
||||
sigset_t omask, nmask;
|
||||
WAIT_T stat_loc;
|
||||
register PID_T pid;
|
||||
|
||||
/*
|
||||
* pclose returns -1 if stream is not associated with a
|
||||
* `popened' command, or, if already `pclosed'.
|
||||
*/
|
||||
if (pids == 0 || pids[fdes = fileno(iop)] == 0)
|
||||
return(-1);
|
||||
(void)fclose(iop);
|
||||
sigemptyset(&nmask);
|
||||
sigaddset(&nmask, SIGINT);
|
||||
sigaddset(&nmask, SIGQUIT);
|
||||
sigaddset(&nmask, SIGHUP);
|
||||
sigprocmask(SIG_BLOCK, &nmask, &omask);
|
||||
do {
|
||||
pid = waitpid(pids[fdes], &stat_loc, NULL);
|
||||
} while (pid == -1 && errno == EINTR);
|
||||
(void)sigprocmask(SIG_SETMASK, &omask, NULL);
|
||||
pids[fdes] = 0;
|
||||
return (pid == -1 ? -1 : WEXITSTATUS(stat_loc));
|
||||
}
|
||||
26
src/cmd/cron/putman.sh
Executable file
26
src/cmd/cron/putman.sh
Executable file
@@ -0,0 +1,26 @@
|
||||
#!/bin/sh
|
||||
|
||||
# putman.sh - install a man page according to local custom
|
||||
# vixie 27dec93 [original]
|
||||
#
|
||||
# $Id:$
|
||||
|
||||
PAGE=$1
|
||||
DIR=$2
|
||||
|
||||
SECT=`expr $PAGE : '[a-z]*.\([0-9]\)'`
|
||||
MDIR="$DIR/cat$SECT"
|
||||
DEST="$MDIR/`basename $PAGE .$SECT`.0"
|
||||
|
||||
set -x
|
||||
if [ ! -d $MDIR ]; then
|
||||
rm -f $MDIR
|
||||
mkdir -p $MDIR
|
||||
chmod 755 $MDIR
|
||||
fi
|
||||
|
||||
nroff -man $PAGE >$DEST
|
||||
chmod 644 $DEST
|
||||
set +x
|
||||
|
||||
exit 0
|
||||
102
src/cmd/cron/user.c
Executable file
102
src/cmd/cron/user.c
Executable file
@@ -0,0 +1,102 @@
|
||||
/* Copyright 1988,1990,1993,1994 by Paul Vixie
|
||||
* All rights reserved
|
||||
*
|
||||
* Distribute freely, except: don't remove my name from the source or
|
||||
* documentation (don't take credit for my work), mark your changes (don't
|
||||
* get me blamed for your possible bugs), don't alter or remove this
|
||||
* notice. May be sold if buildable source is provided to buyer. No
|
||||
* warrantee of any kind, express or implied, is included with this
|
||||
* software; use at your own risk, responsibility for damages (if any) to
|
||||
* anyone resulting from the use of this software rests entirely with the
|
||||
* user.
|
||||
*
|
||||
* Send bug reports, bug fixes, enhancements, requests, flames, etc., and
|
||||
* I'll try to keep a version up to date. I can be reached as follows:
|
||||
* Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
|
||||
*/
|
||||
|
||||
#if !defined(lint) && !defined(LINT)
|
||||
static char rcsid[] = "$Id: user.c,v 2.8 1994/01/15 20:43:43 vixie Exp $";
|
||||
#endif
|
||||
|
||||
/* vix 26jan87 [log is in RCS file]
|
||||
*/
|
||||
|
||||
|
||||
#include "cron.h"
|
||||
|
||||
|
||||
void
|
||||
free_user(u)
|
||||
user *u;
|
||||
{
|
||||
register entry *e, *ne;
|
||||
|
||||
free(u->name);
|
||||
for (e = u->crontab; e != NULL; e = ne) {
|
||||
ne = e->next;
|
||||
free_entry(e);
|
||||
}
|
||||
free(u);
|
||||
}
|
||||
|
||||
|
||||
user *
|
||||
load_user(crontab_fd, pw, name)
|
||||
int crontab_fd;
|
||||
struct passwd *pw; /* NULL implies syscrontab */
|
||||
char *name;
|
||||
{
|
||||
char envstr[MAX_ENVSTR];
|
||||
FILE *file;
|
||||
register user *u;
|
||||
register entry *e;
|
||||
int status;
|
||||
char **envp;
|
||||
|
||||
if (!(file = fdopen(crontab_fd, "r"))) {
|
||||
perror("fdopen on crontab_fd in load_user");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Debug(DPARS, ("load_user()\n"))
|
||||
|
||||
/* file is open. build user entry, then read the crontab file.
|
||||
*/
|
||||
u = (user *) malloc(sizeof(user));
|
||||
u->name = strdup(name);
|
||||
u->crontab = NULL;
|
||||
|
||||
/*
|
||||
* init environment. this will be copied/augmented for each entry.
|
||||
*/
|
||||
envp = env_init();
|
||||
|
||||
/*
|
||||
* load the crontab
|
||||
*/
|
||||
while ((status = load_env(envstr, file)) >= OK) {
|
||||
switch (status) {
|
||||
case ERR:
|
||||
free_user(u);
|
||||
u = NULL;
|
||||
goto done;
|
||||
case FALSE:
|
||||
e = load_entry(file, NULL, pw, envp);
|
||||
if (e) {
|
||||
e->next = u->crontab;
|
||||
u->crontab = e;
|
||||
}
|
||||
break;
|
||||
case TRUE:
|
||||
envp = env_set(envp, envstr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
env_free(envp);
|
||||
fclose(file);
|
||||
Debug(DPARS, ("...load_user() done\n"))
|
||||
return u;
|
||||
}
|
||||
62
src/cmd/lccom-1/CPYRIGHT
Normal file
62
src/cmd/lccom-1/CPYRIGHT
Normal file
@@ -0,0 +1,62 @@
|
||||
The authors of this software are Christopher W. Fraser and
|
||||
David R. Hanson.
|
||||
|
||||
Copyright (c) 1991,1992,1993,1994,1995,1996,1997,1998,1999,2000,2001,2002
|
||||
by AT&T, Christopher W. Fraser, and David R. Hanson. All Rights Reserved.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose, subject to the provisions described below, without fee is
|
||||
hereby granted, provided that this entire notice is included in all
|
||||
copies of any software that is or includes a copy or modification of
|
||||
this software and in all copies of the supporting documentation for
|
||||
such software.
|
||||
|
||||
THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
|
||||
WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY
|
||||
REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
|
||||
OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
|
||||
|
||||
|
||||
lcc is not public-domain software, shareware, and it is not protected
|
||||
by a `copyleft' agreement, like the code from the Free Software
|
||||
Foundation.
|
||||
|
||||
lcc is available free for your personal research and instructional use
|
||||
under the `fair use' provisions of the copyright law. You may, however,
|
||||
redistribute lcc in whole or in part provided you acknowledge its
|
||||
source and include this CPYRIGHT file. You may, for example, include
|
||||
the distribution in a CDROM of free software, provided you charge only
|
||||
for the media, or mirror the distribution files at your site.
|
||||
|
||||
You may not sell lcc or any product derived from it in which it is a
|
||||
significant part of the value of the product. Using the lcc front end
|
||||
to build a C syntax checker is an example of this kind of product.
|
||||
|
||||
You may use parts of lcc in products as long as you charge for only
|
||||
those components that are entirely your own and you acknowledge the use
|
||||
of lcc clearly in all product documentation and distribution media. You
|
||||
must state clearly that your product uses or is based on parts of lcc
|
||||
and that lcc is available free of charge. You must also request that
|
||||
bug reports on your product be reported to you. Using the lcc front
|
||||
end to build a C compiler for the Motorola 88000 chip and charging for
|
||||
and distributing only the 88000 code generator is an example of this
|
||||
kind of product.
|
||||
|
||||
Using parts of lcc in other products is more problematic. For example,
|
||||
using parts of lcc in a C++ compiler could save substantial time and
|
||||
effort and therefore contribute significantly to the profitability of
|
||||
the product. This kind of use, or any use where others stand to make a
|
||||
profit from what is primarily our work, requires a license agreement
|
||||
with Addison-Wesley. Per-copy and unlimited use licenses are
|
||||
available; for more information, contact
|
||||
|
||||
Mike Hendrickson
|
||||
Addison Wesley Professional
|
||||
75 Arlington St.
|
||||
Boston, MA 02116
|
||||
617/848-6522 FAX: 617/848-6569 mikeh@awl.com
|
||||
|
||||
-----
|
||||
Chris Fraser / cwfraser@microsoft.com
|
||||
David Hanson / drh@microsoft.com
|
||||
$Revision: 1.8 $ $Date: 2002/08/27 00:02:26 $
|
||||
383
src/cmd/lccom-1/LOG
Normal file
383
src/cmd/lccom-1/LOG
Normal file
@@ -0,0 +1,383 @@
|
||||
From lcc 4.1 to 4.2:
|
||||
|
||||
Below is a summary of the source-control log entries for those files
|
||||
changed for lcc 4.2.
|
||||
$Id: LOG,v 1.26 2002/08/26 23:59:51 drh Exp $
|
||||
|
||||
src/alloc.c:
|
||||
Fixed subtle alignment bug on p. 28.
|
||||
|
||||
src/dag.c:
|
||||
Renamed kill to killnodes to avoid possible clash with kill in <signal.ch>.
|
||||
|
||||
Made address interface function optional.
|
||||
|
||||
Ensured that generated locals, even if not temps, are
|
||||
added to the code list.
|
||||
|
||||
Avoided calls to undag when errcnt>0.
|
||||
|
||||
Changed listnodes so that array types decay to pointers; bug
|
||||
tickled by -n option.
|
||||
|
||||
src/decl.c:
|
||||
Moved call to retcode that injects a return at the end of a function
|
||||
into compound so that temps created by retcode will be
|
||||
scoped properly.
|
||||
|
||||
Fixed bug in which functions that return const struct S {...} issued
|
||||
an incorrect diagnostic.
|
||||
|
||||
Ensure addressed is set for local arrays.
|
||||
|
||||
src/enode.c:
|
||||
Corrected type conversions in addtree for ADD+P nodes;
|
||||
stripped qualifiers from pointer types in eqtree and cmptree.
|
||||
|
||||
src/event.c:
|
||||
Revise functions called at events so all have the same signature,
|
||||
modulo pointer types.
|
||||
|
||||
src/expr.c:
|
||||
With -b, avoid instrumenting constant ?: expressions.
|
||||
|
||||
Fixed bug in wide-character literals.
|
||||
|
||||
Fix allocation error for identifiers injected to quell
|
||||
undeclared identifier errors;
|
||||
Correct int type added to addresses in field offsets.
|
||||
|
||||
src/gen.c:
|
||||
Made emitasm extern.
|
||||
|
||||
src/input.c:
|
||||
Fixed bug in resynch that caused infinite loop.
|
||||
|
||||
Added #ident recognition, which simply ignores it.
|
||||
|
||||
src/symbolic.c:
|
||||
Revised symbolic back end to emit -0.0 correctly.
|
||||
|
||||
Emit flags in symbolic output.
|
||||
|
||||
src/bytecode.c:
|
||||
Fixed double output botch in defconst in bytecode backend.
|
||||
|
||||
src/dagcheck.c:
|
||||
Added mssing cases in dagcheck's reduce for types signedptr
|
||||
and unsignedptr. Without these, reduce can fail.
|
||||
|
||||
src/lex.c:
|
||||
Permit \ to escape newline in string literals.
|
||||
|
||||
Fixed scon() so that it accepts and concatenates adjacent wide-character literals.
|
||||
|
||||
src/main.c:
|
||||
Fixed long-standing bug in which -n, -b, -C, and -a options caused
|
||||
interface functions to be called before progbeg.
|
||||
|
||||
src/mips.md:
|
||||
Fixed FP comparisons to handle NaNs correctly.
|
||||
|
||||
Round up framesize to a multiple of 16 for Irix 6.x;
|
||||
added casts in defconst to discard unused bits.
|
||||
|
||||
src/prof.c:
|
||||
Revise functions called at events so all have the same signature,
|
||||
modulo pointer types.
|
||||
|
||||
Change ftype to build arbitrary prototypes.
|
||||
|
||||
Fixed long-standing bug in which -n, -b, -C, and -a options caused
|
||||
interface functions to be called before progbeg.
|
||||
Also, added missing call to space.
|
||||
|
||||
Relaxed assertion in loop that searches for
|
||||
embedded CALL nodes.
|
||||
|
||||
Emit correct padding in generated -b strutures.
|
||||
|
||||
Revised call hook to find CALL nodes embedded
|
||||
at any depth.
|
||||
|
||||
Edited bbincr so that it doesn't inject increments in constant expressions.
|
||||
|
||||
src/profio.c:
|
||||
Change compare's prototype to conform to ANSI standard;
|
||||
change calls to qsort accordingly.
|
||||
|
||||
Fixed implicit assumption that execution point i in file f always
|
||||
refers to the same x,y. This isn't true for noweb files.
|
||||
|
||||
src/simp.c:
|
||||
Made address interface function optional.
|
||||
|
||||
Tighten test that avoids folding addressing expressions
|
||||
for switch tables when the offset exceeds 16 bits. Without
|
||||
this addition, lcc can emit erroneous "initializer must be
|
||||
constant" diagnostics.
|
||||
|
||||
Revised simplify() so that if doesn't call address to
|
||||
fold (ADDGRP a n) when n exceeds 16 bits.
|
||||
|
||||
Reordered tests in MOD+I case so expressions like 6%1 fold correctly.
|
||||
|
||||
src/sparc.md:
|
||||
Fixed FP comparisons to handle NaNs correctly.
|
||||
|
||||
Added casts in defconst() to emit constants with proper sizes.
|
||||
|
||||
Replaced pseudo-instructions st2/ld2 with pairs of st/ld instructions.
|
||||
|
||||
src/stmt.c:
|
||||
Fixed botch in 2.16 revision.
|
||||
|
||||
Fixed return statement so that "return;" in a function that returns
|
||||
a value returns 0. Similar code is already in decl.nw.
|
||||
|
||||
Resynch to distributable version.
|
||||
|
||||
Used dynamic variables for loop and switch handles.
|
||||
|
||||
Fixed bug in which functions that return const struct S {...} issued
|
||||
an incorrect diagnostic.
|
||||
|
||||
Appended a missing \n to a warning.
|
||||
|
||||
src/sym.c:
|
||||
Revised 2.19's code for comparing -0.0 and 0.0 so that it works
|
||||
correctly when long double's occupy more bytes than the actual
|
||||
values, as on the x86 under gcc.
|
||||
Fix constant() so that it treats -0.0 and +0.0 as different constants.
|
||||
|
||||
Added newtable() to allocate empty symbol tables.
|
||||
|
||||
src/trace.c:
|
||||
Revise functions called at events so all have the same signature,
|
||||
modulo pointer types.
|
||||
|
||||
Change ftype to build arbitrary prototypes.
|
||||
|
||||
Fixed long-standing bug in which -n, -b, -C, and -a options caused
|
||||
interface functions to be called before progbeg.
|
||||
|
||||
src/tree.c:
|
||||
Correct revision 2.9's implementation of check to avoid
|
||||
superfluous diagnostics on widening conversions.
|
||||
|
||||
Avoid diagnostics for superfluous widening conversions.
|
||||
|
||||
src/types.c:
|
||||
Revise fieldmask definition so it works
|
||||
for all values of fieldsize.
|
||||
|
||||
Change ftype to build arbitrary prototypes.
|
||||
|
||||
src/x86.md:
|
||||
Fixed FP comparisons to handle NaNs correctly.
|
||||
|
||||
Changed ARGB instruction sequence to decrement
|
||||
esp by the size of the structure rounded up to 4.
|
||||
Changed struct allignment to 1; this required changing local to
|
||||
insure that on-stack structs are aligned to at least 4 bytes.
|
||||
|
||||
Added cast in defconst to eliminate extraneous bits.
|
||||
|
||||
Fixed float-to-int conversions so they truncate properly;
|
||||
fixed assembler syntax errors on x86/linux.
|
||||
|
||||
Fixed frame size adjustment for frames >= 4096; now calls _chkstk
|
||||
to allocate large frames.
|
||||
|
||||
Fixed overly specific opcode for loading constants.
|
||||
|
||||
Changed add/sub to addl/subl in potentially ambiguous
|
||||
instructions in the x86/linux back end.
|
||||
|
||||
cpp/cpp.c:
|
||||
Added line to fix Nelson Beebe's bug using #defined x X.
|
||||
|
||||
cpp/cpp.h:
|
||||
Revised to use stdio for output.
|
||||
|
||||
Converted to use stdio for input.
|
||||
|
||||
cpp/eval.c:
|
||||
Added evaluation stack overflow checks.
|
||||
|
||||
Included stdio.h.
|
||||
|
||||
cpp/getopt.c:
|
||||
Include <string.h>; cut decl. for strchr.
|
||||
|
||||
cpp/hideset.c:
|
||||
Fixed potential sizeof botches.
|
||||
|
||||
cpp/include.c:
|
||||
Revised to use stdio for output.
|
||||
|
||||
Removed unused wd[].
|
||||
|
||||
Fixed incorrect check of fopen return value.
|
||||
|
||||
Converted to use stdio for input.
|
||||
|
||||
cpp/lex.c:
|
||||
Fixed incorrect test of fd value; fixed comments.
|
||||
|
||||
Converted to use stdio for input.
|
||||
|
||||
cpp/macro.c:
|
||||
Fixed obscure bug that occurs when string literals are stored
|
||||
in read-only memory. -DX attempts to append a null byte to "1".
|
||||
Found by Nelson Beebe.
|
||||
|
||||
Converted to use stdio for input.
|
||||
|
||||
cpp/nlist.c:
|
||||
Removed unused wd[].
|
||||
|
||||
Added #ident; ignored as for #pragma.
|
||||
|
||||
cpp/tokens.c:
|
||||
Revised to use stdio for output.
|
||||
|
||||
Fixed erroneous initialization and check of FILE * value.
|
||||
|
||||
cpp/unix.c:
|
||||
Revised to use stdio for output.
|
||||
|
||||
Fixed erroneous initialization and check of FILE * value.
|
||||
|
||||
Converted to use stdio for input.
|
||||
|
||||
etc/irix.c:
|
||||
Added -D_LONGLONG, because some SGI assume this.
|
||||
Reported by Nelson Beebe.
|
||||
|
||||
Added -32 option to insure O32 object files and libraries are used.
|
||||
|
||||
etc/lcc.c:
|
||||
Fixed -l file so that file doesn't have to exist.
|
||||
|
||||
Changed \ to / at the end of Win32 include paths.
|
||||
|
||||
Added casts and prototypes to make _spawnvp compatible
|
||||
with Win32 version.
|
||||
|
||||
Permit -dynamic -static on all systems, using option to check
|
||||
for validity.
|
||||
Edited help messages.
|
||||
|
||||
Look for -Wf-unsigned_char=1 and added
|
||||
-D__CHAR_IUNSIGNED__ and -U_CHAR_IS_SIGNED to the cpp
|
||||
command line. These options interact with changes to the
|
||||
standard header limits.h to set CHAR_MIN and CHAR_MAX correctly.
|
||||
Look for -Wf-wchar_t=... and define _WCHAR_T_SIZE appropriately,
|
||||
which interacts with stddef.h and stdlib.h to typedef wchar_t correctly.
|
||||
|
||||
Fixed botch in initinputs that cleared LCCINPUTS.
|
||||
|
||||
etc/win32.c:
|
||||
Added oldnames.lib to link command.
|
||||
|
||||
Cut useless -align directive in ld command.
|
||||
|
||||
include/*/*/limits.h:
|
||||
Corrected name: __CHAR_IS_UNSIGNED__ should be __CHAR_UNSIGNED__.
|
||||
|
||||
Define CHAR_MIN/CHAR_MAX depending on defintion of __CHAR_IS_UNSIGNED__.
|
||||
|
||||
include/*/*/locale.h:
|
||||
Change NULL to ((void*)0).
|
||||
|
||||
Protected definition of NULL;
|
||||
defined NULL as 0L on osf systems.
|
||||
|
||||
include/*/*/stdarg.h:
|
||||
Fixed typedef for __va_list so it's protected by #ifdef.
|
||||
|
||||
Fixed missing #endif;
|
||||
protected definition of va_list by _VA_LIST_DEFINED.
|
||||
|
||||
Changed the type of va_list.
|
||||
|
||||
Insured only va_list was conditionally defined.
|
||||
|
||||
Insured _VA_LIST is defined.
|
||||
|
||||
Revised __va_arg macro to handle va_arg(float) correctly;
|
||||
this addition required a static double temporary, which is
|
||||
less than elegant and subject to order-of-evaluation bugs.
|
||||
|
||||
include/*/*/stddef.h:
|
||||
Change NULL to ((void*)0).
|
||||
|
||||
Added _x_T_DEFINED flag macros for Windows compatibility.
|
||||
|
||||
Added code to typedef wchar_t to unsigned char, short, or int
|
||||
depending on the value of _WCHAR_T_SIZE.
|
||||
|
||||
Protected definition of NULL;
|
||||
defined NULL as 0L on osf systems.
|
||||
|
||||
include/*/*/stdio.h:
|
||||
Change NULL to ((void*)0).
|
||||
|
||||
Added _x_T_DEFINED flag macros for Windows compatibility.
|
||||
|
||||
Changed the type of va_list.
|
||||
|
||||
Protected definition of size_t by _SIZE_T_DEFINED;
|
||||
added definition for va_list protected by _VA_LIST.
|
||||
|
||||
Protected definition of NULL;
|
||||
defined NULL as 0L on osf systems.
|
||||
|
||||
include/*/*/stdlib.h:
|
||||
Change NULL to ((void*)0).
|
||||
|
||||
Added _x_T_DEFINED flag macros for Windows compatibility.
|
||||
|
||||
Added code to typedef wchar_t to unsigned char, short, or int
|
||||
depending on the value of _WCHAR_T_SIZE.
|
||||
|
||||
Protected definition of NULL;
|
||||
defined NULL as 0L on osf systems.
|
||||
|
||||
include/*/*/string.h:
|
||||
Change NULL to ((void*)0).
|
||||
|
||||
Added _x_T_DEFINED flag macros for Windows compatibility.
|
||||
|
||||
Protected definition of NULL;
|
||||
defined NULL as 0L on osf systems.
|
||||
|
||||
include/*/*/time.h:
|
||||
Change NULL to ((void*)0).
|
||||
|
||||
Added _x_T_DEFINED flag macros for Windows compatibility.
|
||||
|
||||
Protected definition of NULL;
|
||||
defined NULL as 0L on osf systems.
|
||||
|
||||
lburg/lburg.c:
|
||||
Simplified use of va_arg to accommodate gcc bug.
|
||||
|
||||
Added ()s to avoid bogus compilation error on Linux.
|
||||
|
||||
lib/assert.c:
|
||||
Added EXPORT and default definition.
|
||||
|
||||
lib/bbexit.c:
|
||||
Access _caller via indirection; add _setcallerp.
|
||||
|
||||
Added EXPORT and default definition.
|
||||
|
||||
Changed profiling counters to ints.
|
||||
|
||||
lib/yynull.c:
|
||||
Added EXPORT and default definition.
|
||||
|
||||
tst/cq.c:
|
||||
Added prototypes to function pointer initializations.
|
||||
189
src/cmd/lccom-1/Makefile
Normal file
189
src/cmd/lccom-1/Makefile
Normal file
@@ -0,0 +1,189 @@
|
||||
#
|
||||
# Makefile for lccom
|
||||
#
|
||||
TOPSRC = $(shell cd ../../..; pwd)
|
||||
#include $(TOPSRC)/target.mk
|
||||
#include $(TOPSRC)/cross.mk
|
||||
MACHINE = mips
|
||||
DESTDIR ?= $(TOPSRC)
|
||||
|
||||
# chipKIT PIC32 compiler on Linux
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# Download from https://github.com/jasonkajita/chipKIT-cxx/downloads
|
||||
# and unzip to /usr/local.
|
||||
# Need to copy pic32-tools/pic32mx/include/stdarg.h
|
||||
# to pic32-tools/lib/gcc/pic32mx/4.5.1/include.
|
||||
# MPLABX C32 compiler doesn't support some functionality
|
||||
# we need, so use chipKIT compiler by default.
|
||||
ifndef GCCPREFIX
|
||||
GCCPREFIX = /usr/local/pic32-tools/bin/pic32-
|
||||
LDFLAGS = -Wl,--oformat=elf32-tradlittlemips
|
||||
INCLUDES = -I/usr/local/pic32-tools/lib/gcc/pic32mx/4.5.1/include
|
||||
endif
|
||||
|
||||
CC = $(GCCPREFIX)gcc -mips32r2 -EL -nostdinc -fshort-double -I$(TOPSRC)/include $(INCLUDES)
|
||||
CXX = $(GCCPREFIX)g++ -mips32r2 -EL -nostdinc -fshort-double -I$(TOPSRC)/include $(INCLUDES)
|
||||
LD = $(GCCPREFIX)ld
|
||||
AR = $(GCCPREFIX)ar
|
||||
RANLIB = $(GCCPREFIX)ranlib
|
||||
SIZE = $(GCCPREFIX)size
|
||||
OBJDUMP = $(GCCPREFIX)objdump -mmips:isa32r2
|
||||
AS = $(CC) -x assembler-with-cpp -c
|
||||
YACC = byacc
|
||||
LEX = flex
|
||||
INSTALL = install -m 644
|
||||
INSTALLDIR = install -m 755 -d
|
||||
TAGSFILE = tags
|
||||
MANROFF = nroff -man -h
|
||||
ELF2AOUT = $(TOPSRC)/tools/elf2aout/elf2aout
|
||||
|
||||
CFLAGS = -O -g
|
||||
|
||||
#LDFLAGS += -N -nostartfiles -fno-dwarf2-cfi-asm -T$(TOPSRC)/src/elf32-mips.ld \
|
||||
# $(TOPSRC)/src/crt0.o -L$(TOPSRC)/src
|
||||
|
||||
LIBS = -lc
|
||||
|
||||
### END OF TARGET INCLUDE
|
||||
|
||||
CFLAGS += -Os -Wall -Werror
|
||||
LDFLAGS +=
|
||||
LDFLAGS += -N -nostartfiles -fno-dwarf2-cfi-asm \
|
||||
-L$(TOPSRC)/src
|
||||
LDFLAGS += -T using-bootloader.ld -Wl,-Map=picoc.map
|
||||
|
||||
OBJCOPY = $(GCCPREFIX)objcopy
|
||||
|
||||
OBJS = $Balloc.o $Bbind.o $Bdag.o $Bdagcheck.o $Bdecl.o $Benode.o \
|
||||
$Berror.o $Bexpr.o $Bevent.o $Binit.o $Binits.o $Binput.o \
|
||||
$Blex.o $Blist.o $Boutput.o $Bprof.o $Bprofio.o \
|
||||
$Bsimp.o $Bstmt.o $Bstring.o $Bsym.o $Btrace.o $Btree.o \
|
||||
$Btypes.o $Bnull.o $Bsymbolic.o $Bgen.o $Bbytecode.o \
|
||||
$Bmain.o $Bmips.o
|
||||
|
||||
TARGET = TARGET_MIPS
|
||||
BUILDDIR = build
|
||||
TSTDIR = tst/mips-eb
|
||||
B = $(BUILDDIR)/
|
||||
T = $(TSTDIR)/
|
||||
|
||||
all: build $Blburg lccom.bin liblcc.a
|
||||
|
||||
clean:
|
||||
rm -rf lccom lccom.bin *.a *.dis *~ $B $T/*.1 $T/*.2 $T/*.s
|
||||
rm -f $T8q $Tarray $Tcf $Tcq $Tcvt $Tfields $Tfront $Tincr \
|
||||
$Tinit $Tlimits $Tparanoia $Tsort $Tspill $Tstdarg \
|
||||
$Tstruct $Tswitch $Twf1 $Tyacc
|
||||
|
||||
install: all
|
||||
install lccom.bin $(TOPSRC)/uflash/
|
||||
|
||||
build:
|
||||
mkdir -p $(BUILDDIR)
|
||||
|
||||
#
|
||||
# lccom: the C compiler
|
||||
#
|
||||
lccom.bin: ${OBJS} crt0.o
|
||||
${CC} ${LDFLAGS} -o lccom.elf crt0.o ${OBJS} ${LIBS}
|
||||
${OBJDUMP} -S lccom.elf > lccom.dis
|
||||
${SIZE} lccom.elf
|
||||
$(OBJCOPY) -O binary lccom.elf lccom.bin && rm lccom.elf
|
||||
#$(OBJCOPY) -O ihex --change-addresses=0xa0000000 lccom.elf lccom.hex
|
||||
#${ELF2AOUT} lccom.elf $@ && rm lccom.elf
|
||||
|
||||
$(OBJS): c.h ops.h token.h config.h
|
||||
|
||||
|
||||
$Balloc.o: alloc.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Bbind.o: bind.c; $(CC) $(CFLAGS) -D$(TARGET) -c -I. -o $@ $<
|
||||
$Bdag.o: dag.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Bdecl.o: decl.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Benode.o: enode.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Berror.o: error.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Bevent.o: event.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Bexpr.o: expr.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Bgen.o: gen.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Binit.o: init.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Binits.o: inits.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Binput.o: input.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Blex.o: lex.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Blist.o: list.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Bmain.o: main.c; $(CC) $(CFLAGS) -DVERSION=\"`svnversion`\" -c -I. -o $@ $<
|
||||
$Bnull.o: null.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Boutput.o: output.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Bprof.o: prof.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Bprofio.o: profio.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Bsimp.o: simp.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Bstmt.o: stmt.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Bstring.o: string.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Bsym.o: sym.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Bsymbolic.o: symbolic.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Bbytecode.o: bytecode.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Btrace.o: trace.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Btree.o: tree.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Btypes.o: types.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Bstab.o: stab.c stab.h; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
|
||||
$Bdagcheck.o: $Bdagcheck.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Balpha.o: $Balpha.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Bmips.o: $Bmips.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Bsparc.o: $Bsparc.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Bx86.o: $Bx86.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
$Bx86linux.o: $Bx86linux.c; $(CC) $(CFLAGS) -c -I. -o $@ $<
|
||||
|
||||
$Bdagcheck.c: $Blburg dagcheck.md; $Blburg -n dagcheck.md $@
|
||||
$Balpha.c: $Blburg alpha.md; $Blburg alpha.md $@
|
||||
$Bmips.c: $Blburg mips.md; $Blburg mips.md $@
|
||||
$Bsparc.c: $Blburg sparc.md; $Blburg sparc.md $@
|
||||
$Bx86.c: $Blburg x86.md; $Blburg x86.md $@
|
||||
$Bx86linux.c: $Blburg x86linux.md; $Blburg x86linux.md $@
|
||||
|
||||
#
|
||||
# lburg: code generator generator
|
||||
#
|
||||
$Blburg: $Blburg.o $Bgram.o
|
||||
cc -g -o $@ $Blburg.o $Bgram.o
|
||||
|
||||
$Blburg.o $Bgram.o: lburg/lburg.h
|
||||
|
||||
$Blburg.o: lburg/lburg.c; cc -g -Wall -O -c -Ilburg -o $@ $<
|
||||
$Bgram.o: lburg/gram.c; cc -g -Wall -O -c -Ilburg -o $@ $<
|
||||
|
||||
#
|
||||
# liblcc: run time library
|
||||
#
|
||||
LIBOBJS = $Bassert.o $Bbbexit.o $Byynull.o
|
||||
|
||||
liblcc.a: $(LIBOBJS)
|
||||
$(AR) ruv $@ $Bassert.o $Bbbexit.o $Byynull.o; $(RANLIB) $@ || true
|
||||
|
||||
$Bassert.o: lib/assert.c; $(CC) $(CFLAGS) -c -o $@ $<
|
||||
$Byynull.o: lib/yynull.c; $(CC) $(CFLAGS) -c -o $@ $<
|
||||
$Bbbexit.o: lib/bbexit.c; $(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
#
|
||||
# Tests
|
||||
#
|
||||
test: $T8q.s $Tarray.s $Tcf.s $Tcq.s $Tcvt.s $Tfields.s $Tfront.s \
|
||||
$Tincr.s $Tinit.s $Tlimits.s $Tparanoia.s $Tsort.s \
|
||||
$Tspill.s $Tstdarg.s $Tstruct.s $Tswitch.s $Twf1.s $Tyacc.s
|
||||
|
||||
$T8q.s: tst/8q.c tst/8q.0 all; @env BUILDDIR=$(BUILDDIR) TSTDIR=$(TSTDIR) tst/run.sh $@
|
||||
$Tarray.s: tst/array.c tst/array.0 all; @env BUILDDIR=$(BUILDDIR) TSTDIR=$(TSTDIR) tst/run.sh $@
|
||||
$Tcf.s: tst/cf.c tst/cf.0 all; @env BUILDDIR=$(BUILDDIR) TSTDIR=$(TSTDIR) tst/run.sh $@
|
||||
$Tcq.s: tst/cq.c tst/cq.0 all; @env BUILDDIR=$(BUILDDIR) TSTDIR=$(TSTDIR) tst/run.sh $@
|
||||
$Tcvt.s: tst/cvt.c tst/cvt.0 all; @env BUILDDIR=$(BUILDDIR) TSTDIR=$(TSTDIR) tst/run.sh $@
|
||||
$Tfields.s: tst/fields.c tst/fields.0 all; @env BUILDDIR=$(BUILDDIR) TSTDIR=$(TSTDIR) tst/run.sh $@
|
||||
$Tfront.s: tst/front.c tst/front.0 all; @env BUILDDIR=$(BUILDDIR) TSTDIR=$(TSTDIR) tst/run.sh $@
|
||||
$Tincr.s: tst/incr.c tst/incr.0 all; @env BUILDDIR=$(BUILDDIR) TSTDIR=$(TSTDIR) tst/run.sh $@
|
||||
$Tinit.s: tst/init.c tst/init.0 all; @env BUILDDIR=$(BUILDDIR) TSTDIR=$(TSTDIR) tst/run.sh $@
|
||||
$Tlimits.s: tst/limits.c tst/limits.0 all; @env BUILDDIR=$(BUILDDIR) TSTDIR=$(TSTDIR) tst/run.sh $@
|
||||
$Tparanoia.s: tst/paranoia.c tst/paranoia.0 all; @env BUILDDIR=$(BUILDDIR) TSTDIR=$(TSTDIR) tst/run.sh $@
|
||||
$Tsort.s: tst/sort.c tst/sort.0 all; @env BUILDDIR=$(BUILDDIR) TSTDIR=$(TSTDIR) tst/run.sh $@
|
||||
$Tspill.s: tst/spill.c tst/spill.0 all; @env BUILDDIR=$(BUILDDIR) TSTDIR=$(TSTDIR) tst/run.sh $@
|
||||
$Tstdarg.s: tst/stdarg.c tst/stdarg.0 all; @env BUILDDIR=$(BUILDDIR) TSTDIR=$(TSTDIR) tst/run.sh $@
|
||||
$Tstruct.s: tst/struct.c tst/struct.0 all; @env BUILDDIR=$(BUILDDIR) TSTDIR=$(TSTDIR) tst/run.sh $@
|
||||
$Tswitch.s: tst/switch.c tst/switch.0 all; @env BUILDDIR=$(BUILDDIR) TSTDIR=$(TSTDIR) tst/run.sh $@
|
||||
$Twf1.s: tst/wf1.c tst/wf1.0 all; @env BUILDDIR=$(BUILDDIR) TSTDIR=$(TSTDIR) tst/run.sh $@
|
||||
$Tyacc.s: tst/yacc.c tst/yacc.0 all; @env BUILDDIR=$(BUILDDIR) TSTDIR=$(TSTDIR) tst/run.sh $@
|
||||
22
src/cmd/lccom-1/README
Normal file
22
src/cmd/lccom-1/README
Normal file
@@ -0,0 +1,22 @@
|
||||
This hierarchy is the distribution for lcc version 4.2.
|
||||
|
||||
lcc version 3.x is described in the book "A Retargetable C Compiler:
|
||||
Design and Implementation" (Addison-Wesley, 1995, ISBN 0-8053-1670-1).
|
||||
There are significant differences between 3.x and 4.x, most notably in
|
||||
the intermediate code. For details, see
|
||||
http://www.research.microsoft.com/~drh/pubs/interface4.pdf.
|
||||
|
||||
VERSION 4.2 IS INCOMPATIBLE WITH EARLIER VERSIONS OF LCC. DO NOT
|
||||
UNLOAD THIS DISTRIBUTION ON TOP OF A 3.X DISTRIBUTION.
|
||||
|
||||
LOG describes the changes since the last release.
|
||||
|
||||
CPYRIGHT describes the conditions under you can use, copy, modify, and
|
||||
distribute lcc or works derived from lcc.
|
||||
|
||||
doc/install.html is an HTML file that gives a complete description of
|
||||
the distribution and installation instructions.
|
||||
|
||||
Chris Fraser / cwfraser@microsoft.com
|
||||
David Hanson / drh@microsoft.com
|
||||
$Revision: 2.7 $ $Date: 2003/06/19 22:07:06 $
|
||||
101
src/cmd/lccom-1/alloc.c
Normal file
101
src/cmd/lccom-1/alloc.c
Normal file
@@ -0,0 +1,101 @@
|
||||
#include "c.h"
|
||||
|
||||
struct block {
|
||||
struct block *next;
|
||||
char *limit;
|
||||
char *avail;
|
||||
};
|
||||
|
||||
union align {
|
||||
long l;
|
||||
char *p;
|
||||
double d;
|
||||
int (*f)(void);
|
||||
};
|
||||
|
||||
union header {
|
||||
struct block b;
|
||||
union align a;
|
||||
};
|
||||
|
||||
#ifdef PURIFY
|
||||
|
||||
union header *arena[3];
|
||||
|
||||
void *allocate(unsigned long n, unsigned a) {
|
||||
union header *new = malloc(sizeof *new + n);
|
||||
|
||||
assert(a < NELEMS(arena));
|
||||
if (new == NULL) {
|
||||
error("insufficient memory\n");
|
||||
exit(1);
|
||||
}
|
||||
new->b.next = (void *)arena[a];
|
||||
arena[a] = new;
|
||||
return new + 1;
|
||||
}
|
||||
|
||||
void deallocate(unsigned a) {
|
||||
union header *p, *q;
|
||||
|
||||
assert(a < NELEMS(arena));
|
||||
for (p = arena[a]; p; p = q) {
|
||||
q = (void *)p->b.next;
|
||||
free(p);
|
||||
}
|
||||
arena[a] = NULL;
|
||||
}
|
||||
|
||||
void *newarray(unsigned long m, unsigned long n, unsigned a) {
|
||||
return allocate(m*n, a);
|
||||
}
|
||||
#else
|
||||
static struct block
|
||||
first[] = { { NULL }, { NULL }, { NULL } },
|
||||
*arena[] = { &first[0], &first[1], &first[2] };
|
||||
|
||||
static struct block *freeblocks;
|
||||
|
||||
void *allocate(unsigned long n, unsigned a) {
|
||||
struct block *ap;
|
||||
|
||||
assert(a < NELEMS(arena));
|
||||
assert(n > 0);
|
||||
ap = arena[a];
|
||||
n = roundup(n, sizeof (union align));
|
||||
while (n > ap->limit - ap->avail) {
|
||||
if ((ap->next = freeblocks) != NULL) {
|
||||
freeblocks = freeblocks->next;
|
||||
ap = ap->next;
|
||||
} else
|
||||
{
|
||||
unsigned m = sizeof (union header) + n + roundup(3*1024, sizeof (union align));
|
||||
ap->next = malloc(m);
|
||||
ap = ap->next;
|
||||
if (ap == NULL) {
|
||||
error("insufficient memory\n");
|
||||
exit(1);
|
||||
}
|
||||
ap->limit = (char *)ap + m;
|
||||
}
|
||||
ap->avail = (char *)((union header *)ap + 1);
|
||||
ap->next = NULL;
|
||||
arena[a] = ap;
|
||||
|
||||
}
|
||||
ap->avail += n;
|
||||
return ap->avail - n;
|
||||
}
|
||||
|
||||
void *newarray(unsigned long m, unsigned long n, unsigned a) {
|
||||
return allocate(m*n, a);
|
||||
}
|
||||
|
||||
void deallocate(unsigned a) {
|
||||
assert(a < NELEMS(arena));
|
||||
arena[a]->next = freeblocks;
|
||||
freeblocks = first[a].next;
|
||||
first[a].next = NULL;
|
||||
arena[a] = &first[a];
|
||||
}
|
||||
#endif
|
||||
1188
src/cmd/lccom-1/alpha.md
Normal file
1188
src/cmd/lccom-1/alpha.md
Normal file
File diff suppressed because it is too large
Load Diff
40
src/cmd/lccom-1/bind.c
Normal file
40
src/cmd/lccom-1/bind.c
Normal file
@@ -0,0 +1,40 @@
|
||||
#include "c.h"
|
||||
|
||||
extern Interface symbolic64IR;
|
||||
extern Interface symbolicIR;
|
||||
extern Interface bytecodeIR;
|
||||
extern Interface nullIR;
|
||||
|
||||
extern Interface alphaIR;
|
||||
extern Interface mipsebIR;
|
||||
extern Interface mipselIR;
|
||||
extern Interface sparcIR;
|
||||
extern Interface solarisIR;
|
||||
extern Interface x86IR;
|
||||
extern Interface x86linuxIR;
|
||||
|
||||
Binding bindings[] = {
|
||||
#ifdef TARGET_ALPHA
|
||||
{ "alpha-osf", &alphaIR },
|
||||
#endif
|
||||
#ifdef TARGET_MIPS
|
||||
{ "mips-eb", &mipsebIR },
|
||||
{ "mips-el", &mipselIR },
|
||||
#endif
|
||||
#ifdef TARGET_SPARC
|
||||
{ "sparc-sun", &sparcIR },
|
||||
#endif
|
||||
#ifdef TARGET_SOLARIS
|
||||
{ "sparc-solaris", &solarisIR },
|
||||
#endif
|
||||
#ifdef TARGET_X86
|
||||
{ "x86-win32", &x86IR },
|
||||
{ "x86-linux", &x86linuxIR },
|
||||
#endif
|
||||
{ "symbolic64", &symbolic64IR },
|
||||
{ "symbolic", &symbolicIR },
|
||||
{ "bytecode", &bytecodeIR },
|
||||
{ "null", &nullIR },
|
||||
|
||||
{ NULL, NULL },
|
||||
};
|
||||
287
src/cmd/lccom-1/bytecode.c
Normal file
287
src/cmd/lccom-1/bytecode.c
Normal file
@@ -0,0 +1,287 @@
|
||||
#include "c.h"
|
||||
#define I(f) b_##f
|
||||
|
||||
static void I(segment)(int n) {
|
||||
static int cseg;
|
||||
|
||||
if (cseg != n)
|
||||
switch (cseg = n) {
|
||||
case CODE: print("code\n"); return;
|
||||
case DATA: print("data\n"); return;
|
||||
case BSS: print("bss\n"); return;
|
||||
case LIT: print("lit\n"); return;
|
||||
default: assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void I(address)(Symbol q, Symbol p, long n) {
|
||||
q->x.name = stringf("%s%s%D", p->x.name, n > 0 ? "+" : "", n);
|
||||
}
|
||||
|
||||
static void I(defaddress)(Symbol p) {
|
||||
print("address %s\n", p->x.name);
|
||||
}
|
||||
|
||||
static void I(defconst)(int suffix, int size, Value v) {
|
||||
switch (suffix) {
|
||||
case I:
|
||||
if (size > sizeof (int))
|
||||
print("byte %d %D\n", size, v.i);
|
||||
else
|
||||
print("byte %d %d\n", size, v.i);
|
||||
return;
|
||||
case U:
|
||||
if (size > sizeof (unsigned))
|
||||
print("byte %d %U\n", size, v.u);
|
||||
else
|
||||
print("byte %d %u\n", size, v.u);
|
||||
return;
|
||||
case P: print("byte %d %U\n", size, (unsigned long)v.p); return;
|
||||
case F:
|
||||
if (size == 4) {
|
||||
union {
|
||||
float f32;
|
||||
unsigned u32;
|
||||
} u;
|
||||
u.f32 = v.d;
|
||||
print("byte 4 %u\n", u.u32);
|
||||
} else {
|
||||
union {
|
||||
double d64;
|
||||
unsigned u32[2];
|
||||
} u;
|
||||
u.d64 = v.d;
|
||||
print("byte 4 %u\n", u.u32[swap]);
|
||||
print("byte 4 %u\n", u.u32[1 - swap]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
assert(0);
|
||||
}
|
||||
|
||||
static void I(defstring)(int len, char *str) {
|
||||
char *s;
|
||||
|
||||
for (s = str; s < str + len; s++)
|
||||
print("byte 1 %d\n", (*s)&0377);
|
||||
}
|
||||
|
||||
static void I(defsymbol)(Symbol p) {
|
||||
if (p->scope == CONSTANTS)
|
||||
switch (optype(ttob(p->type))) {
|
||||
case I: p->x.name = stringf("%D", p->u.c.v.i); break;
|
||||
case U: p->x.name = stringf("%U", p->u.c.v.u); break;
|
||||
case P: p->x.name = stringf("%U", p->u.c.v.p); break;
|
||||
default: assert(0);
|
||||
}
|
||||
else if (p->scope >= LOCAL && p->sclass == STATIC)
|
||||
p->x.name = stringf("$%d", genlabel(1));
|
||||
else if (p->scope == LABELS || p->generated)
|
||||
p->x.name = stringf("$%s", p->name);
|
||||
else
|
||||
p->x.name = p->name;
|
||||
}
|
||||
|
||||
static void dumptree(Node p) {
|
||||
switch (specific(p->op)) {
|
||||
case ASGN+B:
|
||||
assert(p->kids[0]);
|
||||
assert(p->kids[1]);
|
||||
assert(p->syms[0]);
|
||||
dumptree(p->kids[0]);
|
||||
dumptree(p->kids[1]);
|
||||
print("%s %d\n", opname(p->op), p->syms[0]->u.c.v.u);
|
||||
return;
|
||||
case RET+V:
|
||||
assert(!p->kids[0]);
|
||||
assert(!p->kids[1]);
|
||||
print("%s\n", opname(p->op));
|
||||
return;
|
||||
}
|
||||
switch (generic(p->op)) {
|
||||
case CNST: case ADDRG: case ADDRF: case ADDRL: case LABEL:
|
||||
assert(!p->kids[0]);
|
||||
assert(!p->kids[1]);
|
||||
assert(p->syms[0] && p->syms[0]->x.name);
|
||||
print("%s %s\n", opname(p->op), p->syms[0]->x.name);
|
||||
return;
|
||||
case CVF: case CVI: case CVP: case CVU:
|
||||
assert(p->kids[0]);
|
||||
assert(!p->kids[1]);
|
||||
assert(p->syms[0]);
|
||||
dumptree(p->kids[0]);
|
||||
print("%s %d\n", opname(p->op), p->syms[0]->u.c.v.i);
|
||||
return;
|
||||
case ARG: case BCOM: case NEG: case INDIR: case JUMP: case RET:
|
||||
assert(p->kids[0]);
|
||||
assert(!p->kids[1]);
|
||||
dumptree(p->kids[0]);
|
||||
print("%s\n", opname(p->op));
|
||||
return;
|
||||
case CALL:
|
||||
assert(p->kids[0]);
|
||||
assert(!p->kids[1]);
|
||||
assert(optype(p->op) != B);
|
||||
dumptree(p->kids[0]);
|
||||
print("%s\n", opname(p->op));
|
||||
return;
|
||||
case ASGN: case BOR: case BAND: case BXOR: case RSH: case LSH:
|
||||
case ADD: case SUB: case DIV: case MUL: case MOD:
|
||||
assert(p->kids[0]);
|
||||
assert(p->kids[1]);
|
||||
dumptree(p->kids[0]);
|
||||
dumptree(p->kids[1]);
|
||||
print("%s\n", opname(p->op));
|
||||
return;
|
||||
case EQ: case NE: case GT: case GE: case LE: case LT:
|
||||
assert(p->kids[0]);
|
||||
assert(p->kids[1]);
|
||||
assert(p->syms[0]);
|
||||
assert(p->syms[0]->x.name);
|
||||
dumptree(p->kids[0]);
|
||||
dumptree(p->kids[1]);
|
||||
print("%s %s\n", opname(p->op), p->syms[0]->x.name);
|
||||
return;
|
||||
}
|
||||
assert(0);
|
||||
}
|
||||
|
||||
static void I(emit)(Node p) {
|
||||
for (; p; p = p->link)
|
||||
dumptree(p);
|
||||
}
|
||||
|
||||
static void I(export)(Symbol p) {
|
||||
print("export %s\n", p->x.name);
|
||||
}
|
||||
|
||||
static void I(function)(Symbol f, Symbol caller[], Symbol callee[], int ncalls) {
|
||||
int i;
|
||||
|
||||
(*IR->segment)(CODE);
|
||||
offset = 0;
|
||||
for (i = 0; caller[i] && callee[i]; i++) {
|
||||
offset = roundup(offset, caller[i]->type->align);
|
||||
caller[i]->x.name = callee[i]->x.name = stringf("%d", offset);
|
||||
caller[i]->x.offset = callee[i]->x.offset = offset;
|
||||
offset += caller[i]->type->size;
|
||||
}
|
||||
maxargoffset = maxoffset = argoffset = offset = 0;
|
||||
gencode(caller, callee);
|
||||
print("proc %s %d %d\n", f->x.name, maxoffset, maxargoffset);
|
||||
emitcode();
|
||||
print("endproc %s %d %d\n", f->x.name, maxoffset, maxargoffset);
|
||||
|
||||
}
|
||||
|
||||
static void gen02(Node p) {
|
||||
assert(p);
|
||||
if (generic(p->op) == ARG) {
|
||||
assert(p->syms[0]);
|
||||
argoffset += (p->syms[0]->u.c.v.i < 4 ? 4 : p->syms[0]->u.c.v.i);
|
||||
} else if (generic(p->op) == CALL) {
|
||||
maxargoffset = (argoffset > maxargoffset ? argoffset : maxargoffset);
|
||||
argoffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void gen01(Node p) {
|
||||
if (p) {
|
||||
gen01(p->kids[0]);
|
||||
gen01(p->kids[1]);
|
||||
gen02(p);
|
||||
}
|
||||
}
|
||||
|
||||
static Node I(gen)(Node p) {
|
||||
Node q;
|
||||
|
||||
assert(p);
|
||||
for (q = p; q; q = q->link)
|
||||
gen01(q);
|
||||
return p;
|
||||
}
|
||||
|
||||
static void I(global)(Symbol p) {
|
||||
print("align %d\n", p->type->align > 4 ? 4 : p->type->align);
|
||||
print("LABELV %s\n", p->x.name);
|
||||
}
|
||||
|
||||
static void I(import)(Symbol p) {
|
||||
print("import %s\n", p->x.name);
|
||||
}
|
||||
|
||||
static void I(local)(Symbol p) {
|
||||
offset = roundup(offset, p->type->align);
|
||||
p->x.name = stringf("%d", offset);
|
||||
p->x.offset = offset;
|
||||
offset += p->type->size;
|
||||
}
|
||||
|
||||
static void I(progbeg)(int argc, char *argv[]) {}
|
||||
|
||||
static void I(progend)(void) {}
|
||||
|
||||
static void I(space)(int n) {
|
||||
print("skip %d\n", n);
|
||||
}
|
||||
|
||||
static void I(stabline)(Coordinate *cp) {
|
||||
static char *prevfile;
|
||||
static int prevline;
|
||||
|
||||
if (cp->file && (prevfile == NULL || strcmp(prevfile, cp->file) != 0)) {
|
||||
print("file \"%s\"\n", prevfile = cp->file);
|
||||
prevline = 0;
|
||||
}
|
||||
if (cp->y != prevline)
|
||||
print("line %d\n", prevline = cp->y);
|
||||
}
|
||||
|
||||
#define b_blockbeg blockbeg
|
||||
#define b_blockend blockend
|
||||
|
||||
Interface bytecodeIR = {
|
||||
{ 1, 1, 0 }, /* char */
|
||||
{ 2, 2, 0 }, /* short */
|
||||
{ 4, 4, 0 }, /* int */
|
||||
{ 4, 4, 0 }, /* long */
|
||||
{ 4, 4, 0 }, /* long long */
|
||||
{ 4, 4, 1 }, /* float */
|
||||
{ 8, 8, 1 }, /* double */
|
||||
{ 8, 8, 1 }, /* long double */
|
||||
{ 4, 4, 0 }, /* T* */
|
||||
{ 0, 4, 0 }, /* struct */
|
||||
0, /* little_endian */
|
||||
0, /* mulops_calls */
|
||||
0, /* wants_callb */
|
||||
0, /* wants_argb */
|
||||
1, /* left_to_right */
|
||||
0, /* wants_dag */
|
||||
0, /* unsigned_char */
|
||||
I(address),
|
||||
I(blockbeg),
|
||||
I(blockend),
|
||||
I(defaddress),
|
||||
I(defconst),
|
||||
I(defstring),
|
||||
I(defsymbol),
|
||||
I(emit),
|
||||
I(export),
|
||||
I(function),
|
||||
I(gen),
|
||||
I(global),
|
||||
I(import),
|
||||
I(local),
|
||||
I(progbeg),
|
||||
I(progend),
|
||||
I(segment),
|
||||
I(space),
|
||||
0, /* I(stabblock) */
|
||||
0, /* I(stabend) */
|
||||
0, /* I(stabfend) */
|
||||
0, /* I(stabinit) */
|
||||
I(stabline),
|
||||
0, /* I(stabsym) */
|
||||
0, /* I(stabtype) */
|
||||
};
|
||||
598
src/cmd/lccom-1/c.h
Normal file
598
src/cmd/lccom-1/c.h
Normal file
@@ -0,0 +1,598 @@
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
#define NEW(p,a) ((p) = allocate(sizeof *(p), (a)))
|
||||
#define NEW0(p,a) memset(NEW((p),(a)), 0, sizeof *(p))
|
||||
#define isaddrop(op) (specific(op)==ADDRG+P || specific(op)==ADDRL+P \
|
||||
|| specific(op)==ADDRF+P)
|
||||
|
||||
#define MAXLINE 512
|
||||
#define BUFSIZE 4096
|
||||
|
||||
#define istypename(t,tsym) (kind[t] == CHAR \
|
||||
|| (t == ID && tsym && tsym->sclass == TYPEDEF))
|
||||
#define sizeop(n) ((n)<<10)
|
||||
#define generic(op) ((op)&0x3F0)
|
||||
#define specific(op) ((op)&0x3FF)
|
||||
#define opindex(op) (((op)>>4)&0x3F)
|
||||
#define opkind(op) ((op)&~0x3F0)
|
||||
#define opsize(op) ((op)>>10)
|
||||
#define optype(op) ((op)&0xF)
|
||||
#ifdef __LCC__
|
||||
#ifndef __STDC__
|
||||
#define __STDC__
|
||||
#endif
|
||||
#endif
|
||||
#define NELEMS(a) ((int)(sizeof (a)/sizeof ((a)[0])))
|
||||
#undef roundup
|
||||
#define roundup(x,n) (((x)+((n)-1))&(~((n)-1)))
|
||||
#define mkop(op,ty) (specific((op) + ttob(ty)))
|
||||
|
||||
#define extend(x,ty) ((x)&(1<<(8*(ty)->size-1)) ? (x)|((~0UL)<<(8*(ty)->size-1)) : (x)&ones(8*(ty)->size))
|
||||
#define ones(n) ((n)>=8*sizeof (unsigned long) ? ~0UL : ~((~0UL)<<(n)))
|
||||
|
||||
#define isqual(t) ((t)->op >= CONST)
|
||||
#define unqual(t) (isqual(t) ? (t)->type : (t))
|
||||
|
||||
#define isvolatile(t) ((t)->op == VOLATILE \
|
||||
|| (t)->op == CONST+VOLATILE)
|
||||
#define isconst(t) ((t)->op == CONST \
|
||||
|| (t)->op == CONST+VOLATILE)
|
||||
#define isarray(t) (unqual(t)->op == ARRAY)
|
||||
#define isstruct(t) (unqual(t)->op == STRUCT \
|
||||
|| unqual(t)->op == UNION)
|
||||
#define isunion(t) (unqual(t)->op == UNION)
|
||||
#define isfunc(t) (unqual(t)->op == FUNCTION)
|
||||
#define isptr(t) (unqual(t)->op == POINTER)
|
||||
#define ischar(t) ((t)->size == 1 && isint(t))
|
||||
#define isint(t) (unqual(t)->op == INT \
|
||||
|| unqual(t)->op == UNSIGNED)
|
||||
#define isfloat(t) (unqual(t)->op == FLOAT)
|
||||
#define isarith(t) (unqual(t)->op <= UNSIGNED)
|
||||
#define isunsigned(t) (unqual(t)->op == UNSIGNED)
|
||||
#define isscalar(t) (unqual(t)->op <= POINTER \
|
||||
|| unqual(t)->op == ENUM)
|
||||
#define isenum(t) (unqual(t)->op == ENUM)
|
||||
#define fieldsize(p) (p)->bitsize
|
||||
#define fieldright(p) ((p)->lsb - 1)
|
||||
#define fieldleft(p) (8*(p)->type->size - \
|
||||
fieldsize(p) - fieldright(p))
|
||||
#define fieldmask(p) (~(fieldsize(p) < 8*unsignedtype->size ? ~0u<<fieldsize(p) : 0u))
|
||||
typedef struct node *Node;
|
||||
|
||||
typedef struct list *List;
|
||||
|
||||
typedef struct code *Code;
|
||||
|
||||
typedef struct swtch *Swtch;
|
||||
|
||||
typedef struct symbol *Symbol;
|
||||
|
||||
typedef struct coord {
|
||||
char *file;
|
||||
unsigned x, y;
|
||||
} Coordinate;
|
||||
typedef struct table *Table;
|
||||
|
||||
typedef union value {
|
||||
long i;
|
||||
unsigned long u;
|
||||
long double d;
|
||||
void *p;
|
||||
void (*g)(void);
|
||||
} Value;
|
||||
typedef struct tree *Tree;
|
||||
|
||||
typedef struct type *Type;
|
||||
|
||||
typedef struct field *Field;
|
||||
|
||||
typedef struct {
|
||||
unsigned printed:1;
|
||||
unsigned marked;
|
||||
unsigned short typeno;
|
||||
void *xt;
|
||||
} Xtype;
|
||||
|
||||
#include "config.h"
|
||||
typedef struct metrics {
|
||||
unsigned char size, align, outofline;
|
||||
} Metrics;
|
||||
typedef struct interface {
|
||||
Metrics charmetric;
|
||||
Metrics shortmetric;
|
||||
Metrics intmetric;
|
||||
Metrics longmetric;
|
||||
Metrics longlongmetric;
|
||||
Metrics floatmetric;
|
||||
Metrics doublemetric;
|
||||
Metrics longdoublemetric;
|
||||
Metrics ptrmetric;
|
||||
Metrics structmetric;
|
||||
unsigned little_endian:1;
|
||||
unsigned mulops_calls:1;
|
||||
unsigned wants_callb:1;
|
||||
unsigned wants_argb:1;
|
||||
unsigned left_to_right:1;
|
||||
unsigned wants_dag:1;
|
||||
unsigned unsigned_char:1;
|
||||
void (*address)(Symbol p, Symbol q, long n);
|
||||
void (*blockbeg)(Env *);
|
||||
void (*blockend)(Env *);
|
||||
void (*defaddress)(Symbol);
|
||||
void (*defconst) (int suffix, int size, Value v);
|
||||
void (*defstring)(int n, char *s);
|
||||
void (*defsymbol)(Symbol);
|
||||
void (*emit) (Node);
|
||||
void (*export)(Symbol);
|
||||
void (*function)(Symbol, Symbol[], Symbol[], int);
|
||||
Node (*gen) (Node);
|
||||
void (*global)(Symbol);
|
||||
void (*import)(Symbol);
|
||||
void (*local)(Symbol);
|
||||
void (*progbeg)(int argc, char *argv[]);
|
||||
void (*progend)(void);
|
||||
void (*segment)(int);
|
||||
void (*space)(int);
|
||||
void (*stabblock)(int, int, Symbol*);
|
||||
void (*stabend) (Coordinate *, Symbol, Coordinate **, Symbol *, Symbol *);
|
||||
void (*stabfend) (Symbol, int);
|
||||
void (*stabinit) (char *, int, char *[]);
|
||||
void (*stabline) (Coordinate *);
|
||||
void (*stabsym) (Symbol);
|
||||
void (*stabtype) (Symbol);
|
||||
Xinterface x;
|
||||
} Interface;
|
||||
typedef struct binding {
|
||||
char *name;
|
||||
Interface *ir;
|
||||
} Binding;
|
||||
|
||||
extern Binding bindings[];
|
||||
extern Interface *IR;
|
||||
typedef struct {
|
||||
List blockentry;
|
||||
List blockexit;
|
||||
List entry;
|
||||
List exit;
|
||||
List returns;
|
||||
List points;
|
||||
List calls;
|
||||
List end;
|
||||
} Events;
|
||||
|
||||
enum {
|
||||
#define xx(a,b,c,d,e,f,g) a=b,
|
||||
#define yy(a,b,c,d,e,f,g)
|
||||
#include "token.h"
|
||||
LAST
|
||||
};
|
||||
struct node {
|
||||
short op;
|
||||
short count;
|
||||
Symbol syms[3];
|
||||
Node kids[2];
|
||||
Node link;
|
||||
Xnode x;
|
||||
};
|
||||
enum {
|
||||
F=FLOAT,
|
||||
I=INT,
|
||||
U=UNSIGNED,
|
||||
P=POINTER,
|
||||
V=VOID,
|
||||
B=STRUCT
|
||||
};
|
||||
#define gop(name,value) name=value<<4,
|
||||
#define op(name,type,sizes)
|
||||
|
||||
enum {
|
||||
#include "ops.h"
|
||||
LASTOP
|
||||
};
|
||||
|
||||
#undef gop
|
||||
#undef op
|
||||
enum { CODE=1, BSS, DATA, LIT };
|
||||
enum { PERM=0, FUNC, STMT };
|
||||
struct list {
|
||||
void *x;
|
||||
List link;
|
||||
};
|
||||
|
||||
struct code {
|
||||
enum { Blockbeg, Blockend, Local, Address, Defpoint,
|
||||
Label, Start, Gen, Jump, Switch
|
||||
} kind;
|
||||
Code prev, next;
|
||||
union {
|
||||
struct {
|
||||
int level;
|
||||
Symbol *locals;
|
||||
Table identifiers, types;
|
||||
Env x;
|
||||
} block;
|
||||
Code begin;
|
||||
Symbol var;
|
||||
|
||||
struct {
|
||||
Symbol sym;
|
||||
Symbol base;
|
||||
long offset;
|
||||
} addr;
|
||||
struct {
|
||||
Coordinate src;
|
||||
int point;
|
||||
} point;
|
||||
Node forest;
|
||||
struct {
|
||||
Symbol sym;
|
||||
Symbol table;
|
||||
Symbol deflab;
|
||||
int size;
|
||||
long *values;
|
||||
Symbol *labels;
|
||||
} swtch;
|
||||
|
||||
} u;
|
||||
};
|
||||
struct swtch {
|
||||
Symbol sym;
|
||||
int lab;
|
||||
Symbol deflab;
|
||||
int ncases;
|
||||
int size;
|
||||
long *values;
|
||||
Symbol *labels;
|
||||
};
|
||||
struct symbol {
|
||||
char *name;
|
||||
int scope;
|
||||
Coordinate src;
|
||||
Symbol up;
|
||||
List uses;
|
||||
int sclass;
|
||||
unsigned structarg:1;
|
||||
|
||||
unsigned addressed:1;
|
||||
unsigned computed:1;
|
||||
unsigned temporary:1;
|
||||
unsigned generated:1;
|
||||
unsigned defined:1;
|
||||
Type type;
|
||||
float ref;
|
||||
union {
|
||||
struct {
|
||||
int label;
|
||||
Symbol equatedto;
|
||||
} l;
|
||||
struct {
|
||||
unsigned cfields:1;
|
||||
unsigned vfields:1;
|
||||
Table ftab; /* omit */
|
||||
Field flist;
|
||||
} s;
|
||||
int value;
|
||||
Symbol *idlist;
|
||||
struct {
|
||||
Value min, max;
|
||||
} limits;
|
||||
struct {
|
||||
Value v;
|
||||
Symbol loc;
|
||||
} c;
|
||||
struct {
|
||||
Coordinate pt;
|
||||
int label;
|
||||
int ncalls;
|
||||
Symbol *callee;
|
||||
} f;
|
||||
int seg;
|
||||
Symbol alias;
|
||||
struct {
|
||||
Node cse;
|
||||
int replace;
|
||||
Symbol next;
|
||||
} t;
|
||||
} u;
|
||||
Xsymbol x;
|
||||
};
|
||||
enum { CONSTANTS=1, LABELS, GLOBAL, PARAM, LOCAL };
|
||||
struct tree {
|
||||
int op;
|
||||
Type type;
|
||||
Tree kids[2];
|
||||
Node node;
|
||||
union {
|
||||
Value v;
|
||||
Symbol sym;
|
||||
|
||||
Field field;
|
||||
} u;
|
||||
};
|
||||
enum {
|
||||
AND=38<<4,
|
||||
NOT=39<<4,
|
||||
OR=40<<4,
|
||||
COND=41<<4,
|
||||
RIGHT=42<<4,
|
||||
FIELD=43<<4
|
||||
};
|
||||
struct type {
|
||||
int op;
|
||||
Type type;
|
||||
int align;
|
||||
int size;
|
||||
union {
|
||||
Symbol sym;
|
||||
struct {
|
||||
unsigned oldstyle:1;
|
||||
Type *proto;
|
||||
} f;
|
||||
} u;
|
||||
Xtype x;
|
||||
};
|
||||
struct field {
|
||||
char *name;
|
||||
Type type;
|
||||
int offset;
|
||||
short bitsize;
|
||||
short lsb;
|
||||
Field link;
|
||||
};
|
||||
extern int assignargs;
|
||||
extern int prunetemps;
|
||||
extern int nodecount;
|
||||
extern Symbol cfunc;
|
||||
extern Symbol retv;
|
||||
extern Tree (*optree[])(int, Tree, Tree);
|
||||
|
||||
extern char kind[];
|
||||
extern int errcnt;
|
||||
extern int errlimit;
|
||||
extern int wflag;
|
||||
extern Events events;
|
||||
extern float refinc;
|
||||
|
||||
extern unsigned char *cp;
|
||||
extern unsigned char *limit;
|
||||
extern char *firstfile;
|
||||
extern char *file;
|
||||
extern char *line;
|
||||
extern int lineno;
|
||||
extern int t;
|
||||
extern char *token;
|
||||
extern Symbol tsym;
|
||||
extern Coordinate src;
|
||||
extern int Aflag;
|
||||
extern int Pflag;
|
||||
extern Symbol YYnull;
|
||||
extern Symbol YYcheck;
|
||||
extern int glevel;
|
||||
extern int xref;
|
||||
|
||||
extern int ncalled;
|
||||
extern int npoints;
|
||||
|
||||
extern int needconst;
|
||||
extern int explicitCast;
|
||||
extern struct code codehead;
|
||||
extern Code codelist;
|
||||
extern Table stmtlabs;
|
||||
extern float density;
|
||||
extern Table constants;
|
||||
extern Table externals;
|
||||
extern Table globals;
|
||||
extern Table identifiers;
|
||||
extern Table labels;
|
||||
extern Table types;
|
||||
extern int level;
|
||||
|
||||
extern List loci, symbols;
|
||||
|
||||
extern List symbols;
|
||||
|
||||
extern int where;
|
||||
extern Type chartype;
|
||||
extern Type doubletype;
|
||||
extern Type floattype;
|
||||
extern Type inttype;
|
||||
extern Type longdouble;
|
||||
extern Type longtype;
|
||||
extern Type longlong;
|
||||
extern Type shorttype;
|
||||
extern Type signedchar;
|
||||
extern Type unsignedchar;
|
||||
extern Type unsignedlonglong;
|
||||
extern Type unsignedlong;
|
||||
extern Type unsignedshort;
|
||||
extern Type unsignedtype;
|
||||
extern Type charptype;
|
||||
extern Type funcptype;
|
||||
extern Type voidptype;
|
||||
extern Type voidtype;
|
||||
extern Type unsignedptr;
|
||||
extern Type signedptr;
|
||||
extern Type widechar;
|
||||
extern void *allocate(unsigned long n, unsigned a);
|
||||
extern void deallocate(unsigned a);
|
||||
extern void *newarray(unsigned long m, unsigned long n, unsigned a);
|
||||
extern void walk(Tree e, int tlab, int flab);
|
||||
extern Node listnodes(Tree e, int tlab, int flab);
|
||||
extern Node newnode(int op, Node left, Node right, Symbol p);
|
||||
extern Tree cvtconst(Tree);
|
||||
extern void printdag(Node, int);
|
||||
extern void compound(int, Swtch, int);
|
||||
extern void defglobal(Symbol, int);
|
||||
extern void finalize(void);
|
||||
extern void program(void);
|
||||
|
||||
extern Tree vcall(Symbol func, Type ty, ...);
|
||||
extern Tree addrof(Tree);
|
||||
extern Tree asgn(Symbol, Tree);
|
||||
extern Tree asgntree(int, Tree, Tree);
|
||||
extern Type assign(Type, Tree);
|
||||
extern Tree bittree(int, Tree, Tree);
|
||||
extern Tree call(Tree, Type, Coordinate);
|
||||
extern Tree calltree(Tree, Type, Tree, Symbol);
|
||||
extern Tree condtree(Tree, Tree, Tree);
|
||||
extern Tree cnsttree(Type, ...);
|
||||
extern Tree consttree(unsigned int, Type);
|
||||
extern Tree eqtree(int, Tree, Tree);
|
||||
extern int iscallb(Tree);
|
||||
extern Tree shtree(int, Tree, Tree);
|
||||
extern void typeerror(int, Tree, Tree);
|
||||
|
||||
extern void test(int tok, char set[]);
|
||||
extern void expect(int tok);
|
||||
extern void skipto(int tok, char set[]);
|
||||
extern void error(const char *, ...);
|
||||
extern int fatal(const char *, const char *, int);
|
||||
extern void warning(const char *, ...);
|
||||
|
||||
typedef void (*Apply)(void *, void *, void *);
|
||||
extern void attach(Apply, void *, List *);
|
||||
extern void apply(List event, void *arg1, void *arg2);
|
||||
extern Tree retype(Tree p, Type ty);
|
||||
extern Tree rightkid(Tree p);
|
||||
extern int hascall(Tree p);
|
||||
extern Type binary(Type, Type);
|
||||
extern Tree cast(Tree, Type);
|
||||
extern Tree cond(Tree);
|
||||
extern Tree expr0(int);
|
||||
extern Tree expr(int);
|
||||
extern Tree expr1(int);
|
||||
extern Tree field(Tree, const char *);
|
||||
extern char *funcname(Tree);
|
||||
extern Tree idtree(Symbol);
|
||||
extern Tree incr(int, Tree, Tree);
|
||||
extern Tree lvalue(Tree);
|
||||
extern Tree nullcall(Type, Symbol, Tree, Tree);
|
||||
extern Tree pointer(Tree);
|
||||
extern Tree rvalue(Tree);
|
||||
extern Tree value(Tree);
|
||||
|
||||
extern void defpointer(Symbol);
|
||||
extern Type initializer(Type, int);
|
||||
extern void swtoseg(int);
|
||||
|
||||
extern void input_init(int, char *[]);
|
||||
extern void fillbuf(void);
|
||||
extern void nextline(void);
|
||||
|
||||
extern int getchr(void);
|
||||
extern int gettok(void);
|
||||
|
||||
extern void emitcode(void);
|
||||
extern void gencode (Symbol[], Symbol[]);
|
||||
extern void fprint(FILE *f, const char *fmt, ...);
|
||||
extern char *stringf(const char *, ...);
|
||||
extern void check(Node);
|
||||
extern void print(const char *, ...);
|
||||
|
||||
extern List append(void *x, List list);
|
||||
extern int length(List list);
|
||||
extern void *ltov (List *list, unsigned a);
|
||||
extern void init(int, char *[]);
|
||||
|
||||
extern Type typename(void);
|
||||
extern void checklab(Symbol p, void *cl);
|
||||
extern Type enumdcl(void);
|
||||
extern void main_init(int, char *[]);
|
||||
extern int main(int, char *[]);
|
||||
|
||||
extern void vfprint(FILE *, char *, const char *, va_list);
|
||||
|
||||
void profInit(char *);
|
||||
extern int process(char *);
|
||||
extern int findfunc(char *, char *);
|
||||
extern int findcount(char *, int, int);
|
||||
|
||||
extern Tree constexpr(int);
|
||||
extern int intexpr(int, int);
|
||||
extern Tree simplify(int, Type, Tree, Tree);
|
||||
extern int ispow2(unsigned long u);
|
||||
|
||||
extern int reachable(int);
|
||||
|
||||
extern void addlocal(Symbol);
|
||||
extern void branch(int);
|
||||
extern Code code(int);
|
||||
extern void definelab(int);
|
||||
extern void definept(Coordinate *);
|
||||
extern void equatelab(Symbol, Symbol);
|
||||
extern Node jump(int);
|
||||
extern void retcode(Tree);
|
||||
extern void statement(int, Swtch, int);
|
||||
extern void swcode(Swtch, int *, int, int);
|
||||
extern void swgen(Swtch);
|
||||
|
||||
extern char * string(const char *str);
|
||||
extern char *stringn(const char *str, int len);
|
||||
extern char *stringd(long n);
|
||||
extern Symbol relocate(const char *name, Table src, Table dst);
|
||||
extern void use(Symbol p, Coordinate src);
|
||||
extern void locus(Table tp, Coordinate *cp);
|
||||
extern Symbol allsymbols(Table);
|
||||
|
||||
extern Symbol constant(Type, Value);
|
||||
extern void enterscope(void);
|
||||
extern void exitscope(void);
|
||||
extern Symbol findlabel(int);
|
||||
extern Symbol findtype(Type);
|
||||
extern void foreach(Table, int, void (*)(Symbol, void *), void *);
|
||||
extern Symbol genident(int, Type, int);
|
||||
extern int genlabel(int);
|
||||
extern Symbol install(const char *, Table *, int, int);
|
||||
extern Symbol intconst(int);
|
||||
extern Symbol lookup(const char *, Table);
|
||||
extern Symbol mkstr(char *);
|
||||
extern Symbol mksymbol(int, const char *, Type);
|
||||
extern Symbol newtemp(int, int, int);
|
||||
extern Table newtable(int);
|
||||
extern Table table(Table, int);
|
||||
extern Symbol temporary(int, Type);
|
||||
extern char *vtoa(Type, Value);
|
||||
|
||||
extern void traceInit(char *);
|
||||
extern int nodeid(Tree);
|
||||
extern char *opname(int);
|
||||
extern int *printed(int);
|
||||
extern void printtree(Tree, int);
|
||||
extern Tree root(Tree);
|
||||
extern Tree texpr(Tree (*)(int), int, int);
|
||||
extern Tree tree(int, Type, Tree, Tree);
|
||||
|
||||
extern void type_init(int, char *[]);
|
||||
|
||||
extern Type signedint(Type);
|
||||
|
||||
extern int hasproto(Type);
|
||||
extern void outtype(Type, FILE *);
|
||||
extern void printdecl (Symbol p, Type ty);
|
||||
extern void printproto(Symbol p, Symbol args[]);
|
||||
extern char *typestring(Type ty, char *id);
|
||||
extern Field fieldref(const char *name, Type ty);
|
||||
extern Type array(Type, int, int);
|
||||
extern Type atop(Type);
|
||||
extern Type btot(int, int);
|
||||
extern Type compose(Type, Type);
|
||||
extern Type deref(Type);
|
||||
extern int eqtype(Type, Type, int);
|
||||
extern Field fieldlist(Type);
|
||||
extern Type freturn(Type);
|
||||
extern Type ftype(Type, ...);
|
||||
extern Type func(Type, Type *, int);
|
||||
extern Field newfield(char *, Type, Type);
|
||||
extern Type newstruct(int, char *);
|
||||
extern void printtype(Type, int);
|
||||
extern Type promote(Type);
|
||||
extern Type ptr(Type);
|
||||
extern Type qual(int, Type);
|
||||
extern void rmtypes(int);
|
||||
extern int ttob(Type);
|
||||
extern int variadic(Type);
|
||||
103
src/cmd/lccom-1/config.h
Normal file
103
src/cmd/lccom-1/config.h
Normal file
@@ -0,0 +1,103 @@
|
||||
typedef struct {
|
||||
unsigned char max_unaligned_load;
|
||||
Symbol (*rmap)(int);
|
||||
|
||||
void (*blkfetch)(int size, int off, int reg, int tmp);
|
||||
void (*blkstore)(int size, int off, int reg, int tmp);
|
||||
void (*blkloop)(int dreg, int doff,
|
||||
int sreg, int soff,
|
||||
int size, int tmps[]);
|
||||
void (*_label)(Node);
|
||||
int (*_rule)(void*, int);
|
||||
short **_nts;
|
||||
void (*_kids)(Node, int, Node*);
|
||||
char **_string;
|
||||
char **_templates;
|
||||
char *_isinstruction;
|
||||
char **_ntname;
|
||||
void (*emit2)(Node);
|
||||
void (*doarg)(Node);
|
||||
void (*target)(Node);
|
||||
void (*clobber)(Node);
|
||||
} Xinterface;
|
||||
extern int askregvar(Symbol, Symbol);
|
||||
extern void blkcopy(int, int, int, int, int, int[]);
|
||||
extern unsigned emitasm(Node, int);
|
||||
extern int getregnum(Node);
|
||||
extern int mayrecalc(Node);
|
||||
extern int mkactual(int, int);
|
||||
extern void mkauto(Symbol);
|
||||
extern Symbol mkreg(char *, int, int, int);
|
||||
extern Symbol mkwildcard(Symbol *);
|
||||
extern int move(Node);
|
||||
extern int notarget(Node);
|
||||
extern void parseflags(int, char **);
|
||||
extern int range(Node, int, int);
|
||||
extern unsigned regloc(Symbol); /* omit */
|
||||
extern void rtarget(Node, int, Symbol);
|
||||
extern void setreg(Node, Symbol);
|
||||
extern void spill(unsigned, int, Node);
|
||||
extern int widens(Node);
|
||||
|
||||
extern int argoffset, maxargoffset;
|
||||
extern int bflag, dflag;
|
||||
extern int dalign, salign;
|
||||
extern int framesize;
|
||||
extern unsigned freemask[], usedmask[];
|
||||
extern int offset, maxoffset;
|
||||
extern int swap;
|
||||
extern unsigned tmask[], vmask[];
|
||||
typedef struct {
|
||||
unsigned listed:1;
|
||||
unsigned registered:1;
|
||||
unsigned emitted:1;
|
||||
unsigned copy:1;
|
||||
unsigned equatable:1;
|
||||
unsigned spills:1;
|
||||
unsigned mayrecalc:1;
|
||||
void *state;
|
||||
short inst;
|
||||
Node kids[3];
|
||||
Node prev, next;
|
||||
Node prevuse;
|
||||
short argno;
|
||||
} Xnode;
|
||||
typedef struct {
|
||||
Symbol vbl;
|
||||
short set;
|
||||
short number;
|
||||
unsigned mask;
|
||||
} *Regnode;
|
||||
enum { IREG=0, FREG=1 };
|
||||
typedef struct {
|
||||
char *name;
|
||||
unsigned int eaddr; /* omit */
|
||||
int offset;
|
||||
Node lastuse;
|
||||
int usecount;
|
||||
Regnode regnode;
|
||||
Symbol *wildcard;
|
||||
} Xsymbol;
|
||||
enum { RX=2 };
|
||||
typedef struct {
|
||||
int offset;
|
||||
unsigned freemask[2];
|
||||
} Env;
|
||||
|
||||
#define LBURG_MAX SHRT_MAX
|
||||
|
||||
enum { VREG=(44<<4) };
|
||||
|
||||
/* Exported for the front end */
|
||||
extern void blockbeg(Env *);
|
||||
extern void blockend(Env *);
|
||||
extern void emit(Node);
|
||||
extern Node gen(Node);
|
||||
|
||||
extern unsigned emitbin(Node, int);
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define debug(x) (void)0
|
||||
#else
|
||||
#define debug(x) (void)(dflag&&((x),0))
|
||||
#endif
|
||||
123
src/cmd/lccom-1/crt0.c
Normal file
123
src/cmd/lccom-1/crt0.c
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) 1987 Regents of the University of California.
|
||||
* All rights reserved. The Berkeley software License Agreement
|
||||
* specifies the terms and conditions for redistribution.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/uflash.h>
|
||||
|
||||
struct uflash_head ufh __attribute__ ((section (".uflash_head"))) =
|
||||
{
|
||||
UFLASH_MAGIC, "lccom"
|
||||
};
|
||||
|
||||
/*
|
||||
* C runtime startoff. When an a.out is loaded by the kernel, the kernel
|
||||
* sets up the stack as follows:
|
||||
*
|
||||
* _________________________________
|
||||
* | (NULL) | top of memory
|
||||
* |-------------------------------|
|
||||
* | |
|
||||
* | environment strings |
|
||||
* | |
|
||||
* |-------------------------------|
|
||||
* | |
|
||||
* | argument strings |
|
||||
* | |
|
||||
* |-------------------------------|
|
||||
* | envv[envc] (NULL) | end of environment vector tag, a 0
|
||||
* |-------------------------------|
|
||||
* | envv[envc-1] | pointer to last environment string
|
||||
* |-------------------------------|
|
||||
* | ... |
|
||||
* |-------------------------------|
|
||||
* | envv[0] | pointer to first environment string
|
||||
* |-------------------------------|
|
||||
* | argv[argc] (NULL) | end of argument vector tag, a 0
|
||||
* |-------------------------------|
|
||||
* | argv[argc-1] | pointer to last argument string
|
||||
* |-------------------------------|
|
||||
* | ... |
|
||||
* |-------------------------------|
|
||||
* | argv[0] | pointer to first argument string
|
||||
* |-------------------------------|
|
||||
* | | space for fourth argument
|
||||
* |-------------------------------|
|
||||
* | | space for third argument
|
||||
* |-------------------------------|
|
||||
* | | space for second argument
|
||||
* |-------------------------------|
|
||||
* sp-> | | space for first
|
||||
* ---------------------------------
|
||||
*
|
||||
* Arguments are passed in registers $a0, $a1 and $a2.
|
||||
* Register $gp is set to the start of data section.
|
||||
*
|
||||
* Crt0 simply moves the env to environ variable, calculates
|
||||
* the __progname and then calls main.
|
||||
*/
|
||||
extern int main (int, char ** /*, char ** */ );
|
||||
|
||||
char **environ;
|
||||
const char *__progname = "";
|
||||
|
||||
void _start (int, char **, char **);
|
||||
|
||||
extern void __data_start();
|
||||
extern void _etext();
|
||||
|
||||
|
||||
/* The entry function. */
|
||||
void
|
||||
_start (argc, argv, env)
|
||||
int argc;
|
||||
char **argv;
|
||||
char **env;
|
||||
{
|
||||
asm volatile ("la $gp, _gp");
|
||||
|
||||
/* Copy the .data image from flash to ram.
|
||||
* Linker places it at the end of .text segment. */
|
||||
extern unsigned _edata;
|
||||
unsigned *src = (unsigned*) &_etext;
|
||||
unsigned *dest = (unsigned*)&__data_start;
|
||||
unsigned *limit = (unsigned*)&_edata; //0x7d03b554 0x7f00b550;
|
||||
|
||||
|
||||
|
||||
extern char _end[];
|
||||
//extern const char *_curbrk;
|
||||
//_curbrk = _end;
|
||||
|
||||
_brk (_end);
|
||||
|
||||
//sbrk((char*) - (char*)dest);
|
||||
//brk((char*)0x7f00f000); //0x7f00c000
|
||||
|
||||
while (dest < limit) {
|
||||
/*printf ("copy %08x from (%08x) to (%08x)\n", *src, src, dest);*/
|
||||
*dest++ = *src++;
|
||||
}
|
||||
//unsigned *bssend = (unsigned*)_end;
|
||||
while( dest < (unsigned*)_end )
|
||||
*dest++ = 0;
|
||||
|
||||
//printf("%p %p %p\n",(char*)&_edata,(char*)&__data_start,(char*)&_edata-(char*)&__data_start);
|
||||
|
||||
environ = env;
|
||||
if (argc > 0 && argv[0] != 0) {
|
||||
const char *s;
|
||||
|
||||
__progname = argv[0];
|
||||
for (s = __progname; *s != '\0'; s++)
|
||||
if (*s == '/')
|
||||
__progname = s + 1;
|
||||
}
|
||||
|
||||
//printf("%p %p %p %p\n",(char*)&_edata,(char*)&__data_start,(char*)&_edata-(char*)&__data_start, _end);
|
||||
|
||||
exit (main (argc, argv));
|
||||
}
|
||||
741
src/cmd/lccom-1/dag.c
Normal file
741
src/cmd/lccom-1/dag.c
Normal file
@@ -0,0 +1,741 @@
|
||||
#include "c.h"
|
||||
|
||||
#define iscall(op) (generic(op) == CALL \
|
||||
|| (IR->mulops_calls \
|
||||
&& (generic(op)==DIV||generic(op)==MOD||generic(op)==MUL) \
|
||||
&& ( optype(op)==U || optype(op)==I)))
|
||||
static Node forest;
|
||||
static struct dag {
|
||||
struct node node;
|
||||
struct dag *hlink;
|
||||
} *buckets[16];
|
||||
int nodecount;
|
||||
static Tree firstarg;
|
||||
int assignargs = 1;
|
||||
int prunetemps = -1;
|
||||
static Node *tail;
|
||||
|
||||
static int depth = 0;
|
||||
static Node replace(Node);
|
||||
static Node prune(Node);
|
||||
static Node asgnnode(Symbol, Node);
|
||||
static struct dag *dagnode(int, Node, Node, Symbol);
|
||||
static Symbol equated(Symbol);
|
||||
static void fixup(Node);
|
||||
static void labelnode(int);
|
||||
static void list(Node);
|
||||
static void killnodes(Symbol);
|
||||
static Node node(int, Node, Node, Symbol);
|
||||
static void printdag1(Node, int, int);
|
||||
static void printnode(Node, int, int);
|
||||
static void reset(void);
|
||||
static Node tmpnode(Node);
|
||||
static void typestab(Symbol, void *);
|
||||
static Node undag(Node);
|
||||
static Node visit(Node, int);
|
||||
static void unlist(void);
|
||||
void walk(Tree tp, int tlab, int flab) {
|
||||
listnodes(tp, tlab, flab);
|
||||
if (forest) {
|
||||
Node list = forest->link;
|
||||
forest->link = NULL;
|
||||
if (!IR->wants_dag && errcnt == 0)
|
||||
list = undag(list);
|
||||
code(Gen)->u.forest = list;
|
||||
forest = NULL;
|
||||
}
|
||||
reset();
|
||||
deallocate(STMT);
|
||||
}
|
||||
|
||||
static Node node(int op, Node l, Node r, Symbol sym) {
|
||||
int i;
|
||||
struct dag *p;
|
||||
|
||||
i = (opindex(op)^((unsigned long)sym>>2))&(NELEMS(buckets)-1);
|
||||
for (p = buckets[i]; p; p = p->hlink)
|
||||
if (p->node.op == op && p->node.syms[0] == sym
|
||||
&& p->node.kids[0] == l && p->node.kids[1] == r)
|
||||
return &p->node;
|
||||
p = dagnode(op, l, r, sym);
|
||||
p->hlink = buckets[i];
|
||||
buckets[i] = p;
|
||||
++nodecount;
|
||||
return &p->node;
|
||||
}
|
||||
static struct dag *dagnode(int op, Node l, Node r, Symbol sym) {
|
||||
struct dag *p;
|
||||
|
||||
NEW0(p, FUNC);
|
||||
p->node.op = op;
|
||||
if ((p->node.kids[0] = l) != NULL)
|
||||
++l->count;
|
||||
if ((p->node.kids[1] = r) != NULL)
|
||||
++r->count;
|
||||
p->node.syms[0] = sym;
|
||||
return p;
|
||||
}
|
||||
Node newnode(int op, Node l, Node r, Symbol sym) {
|
||||
return &dagnode(op, l, r, sym)->node;
|
||||
}
|
||||
static void killnodes(Symbol p) {
|
||||
int i;
|
||||
struct dag **q;
|
||||
|
||||
for (i = 0; i < NELEMS(buckets); i++)
|
||||
for (q = &buckets[i]; *q; )
|
||||
if (generic((*q)->node.op) == INDIR &&
|
||||
(!isaddrop((*q)->node.kids[0]->op)
|
||||
|| (*q)->node.kids[0]->syms[0] == p)) {
|
||||
*q = (*q)->hlink;
|
||||
--nodecount;
|
||||
} else
|
||||
q = &(*q)->hlink;
|
||||
}
|
||||
static void reset(void) {
|
||||
if (nodecount > 0)
|
||||
memset(buckets, 0, sizeof buckets);
|
||||
nodecount = 0;
|
||||
}
|
||||
Node listnodes(Tree tp, int tlab, int flab) {
|
||||
Node p = NULL, l, r;
|
||||
int op;
|
||||
|
||||
assert(tlab || flab || (tlab == 0 && flab == 0));
|
||||
if (tp == NULL)
|
||||
return NULL;
|
||||
if (tp->node)
|
||||
return tp->node;
|
||||
if (isarray(tp->type))
|
||||
op = tp->op + sizeop(voidptype->size);
|
||||
else
|
||||
op = tp->op + sizeop(tp->type->size);
|
||||
switch (generic(tp->op)) {
|
||||
case AND: { if (depth++ == 0) reset();
|
||||
if (flab) {
|
||||
listnodes(tp->kids[0], 0, flab);
|
||||
listnodes(tp->kids[1], 0, flab);
|
||||
} else {
|
||||
listnodes(tp->kids[0], 0, flab = genlabel(1));
|
||||
listnodes(tp->kids[1], tlab, 0);
|
||||
labelnode(flab);
|
||||
}
|
||||
depth--; } break;
|
||||
case OR: { if (depth++ == 0)
|
||||
reset();
|
||||
if (tlab) {
|
||||
listnodes(tp->kids[0], tlab, 0);
|
||||
listnodes(tp->kids[1], tlab, 0);
|
||||
} else {
|
||||
tlab = genlabel(1);
|
||||
listnodes(tp->kids[0], tlab, 0);
|
||||
listnodes(tp->kids[1], 0, flab);
|
||||
labelnode(tlab);
|
||||
}
|
||||
depth--;
|
||||
} break;
|
||||
case NOT: { return listnodes(tp->kids[0], flab, tlab); }
|
||||
case COND: { Tree q = tp->kids[1];
|
||||
assert(tlab == 0 && flab == 0);
|
||||
if (tp->u.sym)
|
||||
addlocal(tp->u.sym);
|
||||
flab = genlabel(2);
|
||||
listnodes(tp->kids[0], 0, flab);
|
||||
assert(q && q->op == RIGHT);
|
||||
reset();
|
||||
listnodes(q->kids[0], 0, 0);
|
||||
if (forest->op == LABEL+V) {
|
||||
equatelab(forest->syms[0], findlabel(flab + 1));
|
||||
unlist();
|
||||
}
|
||||
list(jump(flab + 1));
|
||||
labelnode(flab);
|
||||
listnodes(q->kids[1], 0, 0);
|
||||
if (forest->op == LABEL+V) {
|
||||
equatelab(forest->syms[0], findlabel(flab + 1));
|
||||
unlist();
|
||||
}
|
||||
labelnode(flab + 1);
|
||||
|
||||
if (tp->u.sym)
|
||||
p = listnodes(idtree(tp->u.sym), 0, 0); } break;
|
||||
case CNST: { Type ty = unqual(tp->type);
|
||||
assert(ty->u.sym);
|
||||
if (tlab || flab) {
|
||||
assert(ty == inttype);
|
||||
if (tlab && tp->u.v.i != 0)
|
||||
list(jump(tlab));
|
||||
else if (flab && tp->u.v.i == 0)
|
||||
list(jump(flab));
|
||||
}
|
||||
else if (ty->u.sym->addressed)
|
||||
p = listnodes(cvtconst(tp), 0, 0);
|
||||
else
|
||||
p = node(op, NULL, NULL, constant(ty, tp->u.v)); } break;
|
||||
case RIGHT: { if ( tp->kids[0] && tp->kids[1]
|
||||
&& generic(tp->kids[1]->op) == ASGN
|
||||
&& ((generic(tp->kids[0]->op) == INDIR
|
||||
&& tp->kids[0]->kids[0] == tp->kids[1]->kids[0])
|
||||
|| (tp->kids[0]->op == FIELD
|
||||
&& tp->kids[0] == tp->kids[1]->kids[0]))) {
|
||||
assert(tlab == 0 && flab == 0);
|
||||
if (generic(tp->kids[0]->op) == INDIR) {
|
||||
p = listnodes(tp->kids[0], 0, 0);
|
||||
list(p);
|
||||
listnodes(tp->kids[1], 0, 0);
|
||||
}
|
||||
else {
|
||||
assert(generic(tp->kids[0]->kids[0]->op) == INDIR);
|
||||
list(listnodes(tp->kids[0]->kids[0], 0, 0));
|
||||
p = listnodes(tp->kids[0], 0, 0);
|
||||
listnodes(tp->kids[1], 0, 0);
|
||||
}
|
||||
} else if (tp->kids[1]) {
|
||||
listnodes(tp->kids[0], 0, 0);
|
||||
p = listnodes(tp->kids[1], tlab, flab);
|
||||
} else
|
||||
p = listnodes(tp->kids[0], tlab, flab); } break;
|
||||
case JUMP: { assert(tlab == 0 && flab == 0);
|
||||
assert(tp->u.sym == 0);
|
||||
assert(tp->kids[0]);
|
||||
l = listnodes(tp->kids[0], 0, 0);
|
||||
list(newnode(JUMP+V, l, NULL, NULL));
|
||||
reset(); } break;
|
||||
case CALL: { Tree save = firstarg;
|
||||
firstarg = NULL;
|
||||
assert(tlab == 0 && flab == 0);
|
||||
if (tp->op == CALL+B && !IR->wants_callb) {
|
||||
Tree arg0 = tree(ARG+P, tp->kids[1]->type,
|
||||
tp->kids[1], NULL);
|
||||
if (IR->left_to_right)
|
||||
firstarg = arg0;
|
||||
l = listnodes(tp->kids[0], 0, 0);
|
||||
if (!IR->left_to_right || firstarg) {
|
||||
firstarg = NULL;
|
||||
listnodes(arg0, 0, 0);
|
||||
}
|
||||
p = newnode(CALL+V, l, NULL, NULL);
|
||||
} else {
|
||||
l = listnodes(tp->kids[0], 0, 0);
|
||||
r = listnodes(tp->kids[1], 0, 0);
|
||||
p = newnode(tp->op == CALL+B ? tp->op : op, l, r, NULL);
|
||||
}
|
||||
NEW0(p->syms[0], FUNC);
|
||||
assert(isptr(tp->kids[0]->type));
|
||||
assert(isfunc(tp->kids[0]->type->type));
|
||||
p->syms[0]->type = tp->kids[0]->type->type;
|
||||
list(p);
|
||||
reset();
|
||||
cfunc->u.f.ncalls++;
|
||||
firstarg = save;
|
||||
} break;
|
||||
case ARG: { assert(tlab == 0 && flab == 0);
|
||||
if (IR->left_to_right)
|
||||
listnodes(tp->kids[1], 0, 0);
|
||||
if (firstarg) {
|
||||
Tree arg = firstarg;
|
||||
firstarg = NULL;
|
||||
listnodes(arg, 0, 0);
|
||||
}
|
||||
l = listnodes(tp->kids[0], 0, 0);
|
||||
list(newnode(tp->op == ARG+B ? tp->op : op, l, NULL, NULL));
|
||||
forest->syms[0] = intconst(tp->type->size);
|
||||
forest->syms[1] = intconst(tp->type->align);
|
||||
if (!IR->left_to_right)
|
||||
listnodes(tp->kids[1], 0, 0); } break;
|
||||
case EQ: case NE: case GT: case GE: case LE:
|
||||
case LT: { assert(tp->u.sym == 0);
|
||||
assert(errcnt || tlab || flab);
|
||||
l = listnodes(tp->kids[0], 0, 0);
|
||||
r = listnodes(tp->kids[1], 0, 0);
|
||||
assert(errcnt || opkind(l->op) == opkind(r->op));
|
||||
assert(errcnt || optype(op) == optype(l->op));
|
||||
if (tlab) {
|
||||
assert(flab == 0);
|
||||
list(newnode(generic(tp->op) + opkind(l->op), l, r, findlabel(tlab)));
|
||||
} else if (flab) {
|
||||
switch (generic(tp->op)) {
|
||||
case EQ: op = NE; break;
|
||||
case NE: op = EQ; break;
|
||||
case GT: op = LE; break;
|
||||
case LT: op = GE; break;
|
||||
case GE: op = LT; break;
|
||||
case LE: op = GT; break;
|
||||
default: assert(0);
|
||||
}
|
||||
list(newnode(op + opkind(l->op), l, r, findlabel(flab)));
|
||||
}
|
||||
if (forest && forest->syms[0])
|
||||
forest->syms[0]->ref++; } break;
|
||||
case ASGN: { assert(tlab == 0 && flab == 0);
|
||||
if (tp->kids[0]->op == FIELD) {
|
||||
Tree x = tp->kids[0]->kids[0];
|
||||
Field f = tp->kids[0]->u.field;
|
||||
assert(generic(x->op) == INDIR);
|
||||
reset();
|
||||
l = listnodes(lvalue(x), 0, 0);
|
||||
if (fieldsize(f) < 8*f->type->size) {
|
||||
unsigned int fmask = fieldmask(f);
|
||||
unsigned int mask = fmask<<fieldright(f);
|
||||
Tree q = tp->kids[1];
|
||||
if ((q->op == CNST+I && q->u.v.i == 0) ||
|
||||
(q->op == CNST+U && q->u.v.u == 0)) {
|
||||
q = bittree(BAND, x, cnsttree(unsignedtype, (unsigned long)~mask));
|
||||
} else if ((q->op == CNST+I && (q->u.v.i&fmask) == fmask) ||
|
||||
(q->op == CNST+U && (q->u.v.u&fmask) == fmask)) {
|
||||
q = bittree(BOR, x, cnsttree(unsignedtype, (unsigned long)mask));
|
||||
} else {
|
||||
listnodes(q, 0, 0);
|
||||
q = bittree(BOR,
|
||||
bittree(BAND, rvalue(lvalue(x)),
|
||||
cnsttree(unsignedtype, (unsigned long)~mask)),
|
||||
bittree(BAND, shtree(LSH, cast(q, unsignedtype),
|
||||
cnsttree(unsignedtype, (unsigned long)fieldright(f))),
|
||||
cnsttree(unsignedtype, (unsigned long)mask)));
|
||||
}
|
||||
r = listnodes(q, 0, 0);
|
||||
op = ASGN + ttob(q->type);
|
||||
} else {
|
||||
r = listnodes(tp->kids[1], 0, 0);
|
||||
op = ASGN + ttob(tp->kids[1]->type);
|
||||
}
|
||||
} else {
|
||||
l = listnodes(tp->kids[0], 0, 0);
|
||||
r = listnodes(tp->kids[1], 0, 0);
|
||||
}
|
||||
list(newnode(tp->op == ASGN+B ? tp->op : op, l, r, NULL));
|
||||
forest->syms[0] = intconst(tp->kids[1]->type->size);
|
||||
forest->syms[1] = intconst(tp->kids[1]->type->align);
|
||||
if (isaddrop(tp->kids[0]->op)
|
||||
&& !tp->kids[0]->u.sym->computed)
|
||||
killnodes(tp->kids[0]->u.sym);
|
||||
else
|
||||
reset();
|
||||
p = listnodes(tp->kids[1], 0, 0); } break;
|
||||
case BOR: case BAND: case BXOR:
|
||||
case ADD: case SUB: case RSH:
|
||||
case LSH: { assert(tlab == 0 && flab == 0);
|
||||
l = listnodes(tp->kids[0], 0, 0);
|
||||
r = listnodes(tp->kids[1], 0, 0);
|
||||
p = node(op, l, r, NULL); } break;
|
||||
case DIV: case MUL:
|
||||
case MOD: { assert(tlab == 0 && flab == 0);
|
||||
l = listnodes(tp->kids[0], 0, 0);
|
||||
r = listnodes(tp->kids[1], 0, 0);
|
||||
p = node(op, l, r, NULL);
|
||||
if (IR->mulops_calls && isint(tp->type)) {
|
||||
list(p);
|
||||
cfunc->u.f.ncalls++;
|
||||
} } break;
|
||||
case RET: { assert(tlab == 0 && flab == 0);
|
||||
l = listnodes(tp->kids[0], 0, 0);
|
||||
list(newnode(op, l, NULL, NULL)); } break;
|
||||
case CVF: case CVI: case CVP:
|
||||
case CVU: { assert(tlab == 0 && flab == 0);
|
||||
assert(optype(tp->kids[0]->op) != optype(tp->op) || tp->kids[0]->type->size != tp->type->size);
|
||||
l = listnodes(tp->kids[0], 0, 0);
|
||||
p = node(op, l, NULL, intconst(tp->kids[0]->type->size));
|
||||
} break;
|
||||
case BCOM:
|
||||
case NEG: { assert(tlab == 0 && flab == 0);
|
||||
l = listnodes(tp->kids[0], 0, 0);
|
||||
p = node(op, l, NULL, NULL); } break;
|
||||
case INDIR: { Type ty = tp->kids[0]->type;
|
||||
assert(tlab == 0 && flab == 0);
|
||||
l = listnodes(tp->kids[0], 0, 0);
|
||||
if (isptr(ty))
|
||||
ty = unqual(ty)->type;
|
||||
if (isvolatile(ty)
|
||||
|| (isstruct(ty) && unqual(ty)->u.sym->u.s.vfields))
|
||||
p = newnode(tp->op == INDIR+B ? tp->op : op, l, NULL, NULL);
|
||||
else
|
||||
p = node(tp->op == INDIR+B ? tp->op : op, l, NULL, NULL); } break;
|
||||
case FIELD: { Tree q = tp->kids[0];
|
||||
if (tp->type == inttype) {
|
||||
long n = fieldleft(tp->u.field);
|
||||
q = shtree(RSH,
|
||||
shtree(LSH, q, cnsttree(inttype, n)),
|
||||
cnsttree(inttype, n + fieldright(tp->u.field)));
|
||||
} else if (fieldsize(tp->u.field) < 8*tp->u.field->type->size)
|
||||
q = bittree(BAND,
|
||||
shtree(RSH, q, cnsttree(inttype, (long)fieldright(tp->u.field))),
|
||||
cnsttree(unsignedtype, (unsigned long)fieldmask(tp->u.field)));
|
||||
assert(tlab == 0 && flab == 0);
|
||||
p = listnodes(q, 0, 0); } break;
|
||||
case ADDRG:
|
||||
case ADDRF: { assert(tlab == 0 && flab == 0);
|
||||
p = node(tp->op + sizeop(voidptype->size), NULL, NULL, tp->u.sym);
|
||||
} break;
|
||||
case ADDRL: { assert(tlab == 0 && flab == 0);
|
||||
if (tp->u.sym->generated)
|
||||
addlocal(tp->u.sym);
|
||||
p = node(tp->op + sizeop(voidptype->size), NULL, NULL, tp->u.sym); } break;
|
||||
default:assert(0);
|
||||
}
|
||||
tp->node = p;
|
||||
return p;
|
||||
}
|
||||
static void list(Node p) {
|
||||
if (p && p->link == NULL) {
|
||||
if (forest) {
|
||||
p->link = forest->link;
|
||||
forest->link = p;
|
||||
} else
|
||||
p->link = p;
|
||||
forest = p;
|
||||
}
|
||||
}
|
||||
static void labelnode(int lab) {
|
||||
assert(lab);
|
||||
if (forest && forest->op == LABEL+V)
|
||||
equatelab(findlabel(lab), forest->syms[0]);
|
||||
else
|
||||
list(newnode(LABEL+V, NULL, NULL, findlabel(lab)));
|
||||
reset();
|
||||
}
|
||||
static void unlist(void) {
|
||||
Node p;
|
||||
|
||||
assert(forest);
|
||||
assert(forest != forest->link);
|
||||
p = forest->link;
|
||||
while (p->link != forest)
|
||||
p = p->link;
|
||||
p->link = forest->link;
|
||||
forest = p;
|
||||
}
|
||||
Tree cvtconst(Tree p) {
|
||||
Symbol q = constant(p->type, p->u.v);
|
||||
Tree e;
|
||||
|
||||
if (q->u.c.loc == NULL)
|
||||
q->u.c.loc = genident(STATIC, p->type, GLOBAL);
|
||||
if (isarray(p->type)) {
|
||||
e = simplify(ADDRG, atop(p->type), NULL, NULL);
|
||||
e->u.sym = q->u.c.loc;
|
||||
} else
|
||||
e = idtree(q->u.c.loc);
|
||||
return e;
|
||||
}
|
||||
void gencode(Symbol caller[], Symbol callee[]) {
|
||||
Code cp;
|
||||
Coordinate save;
|
||||
|
||||
if (prunetemps == -1)
|
||||
prunetemps = !IR->wants_dag;
|
||||
save = src;
|
||||
if (assignargs) {
|
||||
int i;
|
||||
Symbol p, q;
|
||||
cp = codehead.next->next;
|
||||
codelist = codehead.next;
|
||||
for (i = 0; (p = callee[i]) != NULL
|
||||
&& (q = caller[i]) != NULL; i++)
|
||||
if (p->sclass != q->sclass || p->type != q->type)
|
||||
walk(asgn(p, idtree(q)), 0, 0);
|
||||
codelist->next = cp;
|
||||
cp->prev = codelist;
|
||||
}
|
||||
if (glevel && IR->stabsym) {
|
||||
int i;
|
||||
Symbol p, q;
|
||||
for (i = 0; (p = callee[i]) != NULL
|
||||
&& (q = caller[i]) != NULL; i++) {
|
||||
(*IR->stabsym)(p);
|
||||
if (p->sclass != q->sclass || p->type != q->type)
|
||||
(*IR->stabsym)(q);
|
||||
}
|
||||
swtoseg(CODE);
|
||||
}
|
||||
cp = codehead.next;
|
||||
for ( ; errcnt <= 0 && cp; cp = cp->next)
|
||||
switch (cp->kind) {
|
||||
case Address: assert(IR->address);
|
||||
(*IR->address)(cp->u.addr.sym, cp->u.addr.base,
|
||||
cp->u.addr.offset); break;
|
||||
case Blockbeg: {
|
||||
Symbol *p = cp->u.block.locals;
|
||||
(*IR->blockbeg)(&cp->u.block.x);
|
||||
for ( ; *p; p++)
|
||||
if ((*p)->ref != 0.0)
|
||||
(*IR->local)(*p);
|
||||
else if (glevel) (*IR->local)(*p);
|
||||
}
|
||||
break;
|
||||
case Blockend: (*IR->blockend)(&cp->u.begin->u.block.x); break;
|
||||
case Defpoint: src = cp->u.point.src; break;
|
||||
case Gen: case Jump:
|
||||
case Label: if (prunetemps)
|
||||
cp->u.forest = prune(cp->u.forest);
|
||||
fixup(cp->u.forest);
|
||||
cp->u.forest = (*IR->gen)(cp->u.forest); break;
|
||||
case Local: (*IR->local)(cp->u.var); break;
|
||||
case Switch: break;
|
||||
default: assert(0);
|
||||
}
|
||||
src = save;
|
||||
}
|
||||
static void fixup(Node p) {
|
||||
for ( ; p; p = p->link)
|
||||
switch (generic(p->op)) {
|
||||
case JUMP:
|
||||
if (specific(p->kids[0]->op) == ADDRG+P)
|
||||
p->kids[0]->syms[0] =
|
||||
equated(p->kids[0]->syms[0]);
|
||||
break;
|
||||
case LABEL: assert(p->syms[0] == equated(p->syms[0])); break;
|
||||
case EQ: case GE: case GT: case LE: case LT: case NE:
|
||||
assert(p->syms[0]);
|
||||
p->syms[0] = equated(p->syms[0]);
|
||||
}
|
||||
}
|
||||
static Symbol equated(Symbol p) {
|
||||
{ Symbol q; for (q = p->u.l.equatedto; q; q = q->u.l.equatedto) assert(p != q); }
|
||||
while (p->u.l.equatedto)
|
||||
p = p->u.l.equatedto;
|
||||
return p;
|
||||
}
|
||||
void emitcode(void) {
|
||||
Code cp;
|
||||
Coordinate save;
|
||||
|
||||
save = src;
|
||||
cp = codehead.next;
|
||||
for ( ; errcnt <= 0 && cp; cp = cp->next)
|
||||
switch (cp->kind) {
|
||||
case Address: break;
|
||||
case Blockbeg: if (glevel && IR->stabblock) {
|
||||
(*IR->stabblock)('{', cp->u.block.level - LOCAL, cp->u.block.locals);
|
||||
swtoseg(CODE);
|
||||
}
|
||||
break;
|
||||
case Blockend: if (glevel && IR->stabblock) {
|
||||
Code bp = cp->u.begin;
|
||||
foreach(bp->u.block.identifiers, bp->u.block.level, typestab, NULL);
|
||||
foreach(bp->u.block.types, bp->u.block.level, typestab, NULL);
|
||||
(*IR->stabblock)('}', bp->u.block.level - LOCAL, bp->u.block.locals);
|
||||
swtoseg(CODE);
|
||||
}
|
||||
break;
|
||||
case Defpoint: src = cp->u.point.src;
|
||||
if (glevel > 0 && IR->stabline) {
|
||||
(*IR->stabline)(&cp->u.point.src); swtoseg(CODE); } break;
|
||||
case Gen: case Jump:
|
||||
case Label: if (cp->u.forest)
|
||||
(*IR->emit)(cp->u.forest); break;
|
||||
case Local: if (glevel && IR->stabsym) {
|
||||
(*IR->stabsym)(cp->u.var);
|
||||
swtoseg(CODE);
|
||||
} break;
|
||||
case Switch: { int i;
|
||||
defglobal(cp->u.swtch.table, LIT);
|
||||
(*IR->defaddress)(equated(cp->u.swtch.labels[0]));
|
||||
for (i = 1; i < cp->u.swtch.size; i++) {
|
||||
long k = cp->u.swtch.values[i-1];
|
||||
while (++k < cp->u.swtch.values[i]) {
|
||||
assert(k < LONG_MAX);
|
||||
(*IR->defaddress)(equated(cp->u.swtch.deflab));
|
||||
}
|
||||
(*IR->defaddress)(equated(cp->u.swtch.labels[i]));
|
||||
}
|
||||
swtoseg(CODE);
|
||||
} break;
|
||||
default: assert(0);
|
||||
}
|
||||
src = save;
|
||||
}
|
||||
|
||||
static Node undag(Node forest) {
|
||||
Node p;
|
||||
|
||||
tail = &forest;
|
||||
for (p = forest; p; p = p->link)
|
||||
if (generic(p->op) == INDIR) {
|
||||
assert(p->count >= 1);
|
||||
visit(p, 1);
|
||||
if (p->syms[2]) {
|
||||
assert(p->syms[2]->u.t.cse);
|
||||
p->syms[2]->u.t.cse = NULL;
|
||||
addlocal(p->syms[2]);
|
||||
}
|
||||
} else if (iscall(p->op) && p->count >= 1)
|
||||
visit(p, 1);
|
||||
else {
|
||||
assert(p->count == 0);
|
||||
visit(p, 1);
|
||||
*tail = p;
|
||||
tail = &p->link;
|
||||
}
|
||||
*tail = NULL;
|
||||
return forest;
|
||||
}
|
||||
static Node replace(Node p) {
|
||||
if (p && ( generic(p->op) == INDIR
|
||||
&& generic(p->kids[0]->op) == ADDRL
|
||||
&& p->kids[0]->syms[0]->temporary
|
||||
&& p->kids[0]->syms[0]->u.t.replace)) {
|
||||
p = p->kids[0]->syms[0]->u.t.cse;
|
||||
if (generic(p->op) == INDIR && isaddrop(p->kids[0]->op))
|
||||
p = newnode(p->op, newnode(p->kids[0]->op, NULL, NULL,
|
||||
p->kids[0]->syms[0]), NULL, NULL);
|
||||
else if (generic(p->op) == ADDRG)
|
||||
p = newnode(p->op, NULL, NULL, p->syms[0]);
|
||||
else
|
||||
assert(0);
|
||||
p->count = 1;
|
||||
} else if (p) {
|
||||
p->kids[0] = replace(p->kids[0]);
|
||||
p->kids[1] = replace(p->kids[1]);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
static Node prune(Node forest) {
|
||||
Node p, *tail = &forest;
|
||||
int count = 0;
|
||||
|
||||
for (p = forest; p; p = p->link) {
|
||||
if (count > 0) {
|
||||
p->kids[0] = replace(p->kids[0]);
|
||||
p->kids[1] = replace(p->kids[1]);
|
||||
}
|
||||
if (( generic(p->op) == ASGN
|
||||
&& generic(p->kids[0]->op) == ADDRL
|
||||
&& p->kids[0]->syms[0]->temporary
|
||||
&& p->kids[0]->syms[0]->u.t.cse == p->kids[1])) {
|
||||
Symbol tmp = p->kids[0]->syms[0];
|
||||
if (!tmp->defined)
|
||||
(*IR->local)(tmp);
|
||||
tmp->defined = 1;
|
||||
if (( generic(p->kids[1]->op) == INDIR
|
||||
&& isaddrop(p->kids[1]->kids[0]->op)
|
||||
&& p->kids[1]->kids[0]->syms[0]->sclass == REGISTER)
|
||||
|| (( generic(p->kids[1]->op) == INDIR
|
||||
&& isaddrop(p->kids[1]->kids[0]->op)) && tmp->sclass == AUTO)
|
||||
|| (generic(p->kids[1]->op) == ADDRG && tmp->sclass == AUTO)) {
|
||||
tmp->u.t.replace = 1;
|
||||
count++;
|
||||
continue; /* and omit the assignment */
|
||||
}
|
||||
}
|
||||
/* keep the assignment and other roots */
|
||||
*tail = p;
|
||||
tail = &(*tail)->link;
|
||||
}
|
||||
assert(*tail == NULL);
|
||||
return forest;
|
||||
}
|
||||
|
||||
static Node visit(Node p, int listed)
|
||||
{
|
||||
if (p) {
|
||||
if (p->syms[2]) {
|
||||
p = tmpnode(p);
|
||||
|
||||
} else if ((p->count <= 1 && !iscall(p->op)) ||
|
||||
(p->count == 0 && iscall(p->op))) {
|
||||
p->kids[0] = visit(p->kids[0], 0);
|
||||
p->kids[1] = visit(p->kids[1], 0);
|
||||
}
|
||||
else if (specific(p->op) == ADDRL+P || specific(p->op) == ADDRF+P) {
|
||||
assert(!listed);
|
||||
p = newnode(p->op, NULL, NULL, p->syms[0]);
|
||||
p->count = 1;
|
||||
}
|
||||
else if (p->op == INDIR+B) {
|
||||
p = newnode(p->op, p->kids[0], NULL, NULL);
|
||||
p->count = 1;
|
||||
p->kids[0] = visit(p->kids[0], 0);
|
||||
p->kids[1] = visit(p->kids[1], 0);
|
||||
}
|
||||
else {
|
||||
p->kids[0] = visit(p->kids[0], 0);
|
||||
p->kids[1] = visit(p->kids[1], 0);
|
||||
p->syms[2] = temporary(REGISTER, btot(p->op, opsize(p->op)));
|
||||
assert(!p->syms[2]->defined);
|
||||
p->syms[2]->ref = 1;
|
||||
p->syms[2]->u.t.cse = p;
|
||||
|
||||
*tail = asgnnode(p->syms[2], p);
|
||||
tail = &(*tail)->link;
|
||||
if (!listed)
|
||||
p = tmpnode(p);
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
static Node tmpnode(Node p) {
|
||||
Symbol tmp = p->syms[2];
|
||||
|
||||
assert(tmp);
|
||||
if (--p->count == 0)
|
||||
p->syms[2] = NULL;
|
||||
p = newnode(INDIR + ttob(tmp->type),
|
||||
newnode(ADDRL + ttob(voidptype), NULL, NULL, tmp), NULL, NULL);
|
||||
p->count = 1;
|
||||
return p;
|
||||
}
|
||||
static Node asgnnode(Symbol tmp, Node p) {
|
||||
p = newnode(ASGN + ttob(tmp->type),
|
||||
newnode(ADDRL + ttob(voidptype), NULL, NULL, tmp), p, NULL);
|
||||
p->syms[0] = intconst(tmp->type->size);
|
||||
p->syms[1] = intconst(tmp->type->align);
|
||||
return p;
|
||||
}
|
||||
/* printdag - print dag p on fd, or the node list if p == 0 */
|
||||
void printdag(Node p, int fd) {
|
||||
FILE *f = fd == 1 ? stdout : stderr;
|
||||
|
||||
printed(0);
|
||||
if (p == 0) {
|
||||
if ((p = forest) != NULL)
|
||||
do {
|
||||
p = p->link;
|
||||
printdag1(p, fd, 0);
|
||||
} while (p != forest);
|
||||
} else if (*printed(nodeid((Tree)p)))
|
||||
fprint(f, "node'%d printed above\n", nodeid((Tree)p));
|
||||
else
|
||||
printdag1(p, fd, 0);
|
||||
}
|
||||
|
||||
/* printdag1 - recursively print dag p */
|
||||
static void printdag1(Node p, int fd, int lev) {
|
||||
int id, i;
|
||||
|
||||
if (p == 0 || *printed(id = nodeid((Tree)p)))
|
||||
return;
|
||||
*printed(id) = 1;
|
||||
for (i = 0; i < NELEMS(p->kids); i++)
|
||||
printdag1(p->kids[i], fd, lev + 1);
|
||||
printnode(p, fd, lev);
|
||||
}
|
||||
|
||||
/* printnode - print fields of dag p */
|
||||
static void printnode(Node p, int fd, int lev) {
|
||||
if (p) {
|
||||
FILE *f = fd == 1 ? stdout : stderr;
|
||||
int i, id = nodeid((Tree)p);
|
||||
fprint(f, "%c%d%s", lev == 0 ? '\'' : '#', id,
|
||||
&" "[id < 10 ? 0 : id < 100 ? 1 : 2]);
|
||||
fprint(f, "%s count=%d", opname(p->op), p->count);
|
||||
for (i = 0; i < NELEMS(p->kids) && p->kids[i]; i++)
|
||||
fprint(f, " #%d", nodeid((Tree)p->kids[i]));
|
||||
if (generic(p->op) == CALL && p->syms[0] && p->syms[0]->type)
|
||||
fprint(f, " {%t}", p->syms[0]->type);
|
||||
else
|
||||
for (i = 0; i < NELEMS(p->syms) && p->syms[i]; i++)
|
||||
if (p->syms[i]->name)
|
||||
fprint(f, " %s", p->syms[i]->name);
|
||||
else
|
||||
fprint(f, " %p", p->syms[i]);
|
||||
fprint(f, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* typestab - emit stab entries for p */
|
||||
static void typestab(Symbol p, void *cl) {
|
||||
if (!isfunc(p->type) && (p->sclass == EXTERN || p->sclass == STATIC) && IR->stabsym)
|
||||
(*IR->stabsym)(p);
|
||||
else if ((p->sclass == TYPEDEF || p->sclass == 0) && IR->stabtype)
|
||||
(*IR->stabtype)(p);
|
||||
}
|
||||
212
src/cmd/lccom-1/dagcheck.md
Normal file
212
src/cmd/lccom-1/dagcheck.md
Normal file
@@ -0,0 +1,212 @@
|
||||
%{
|
||||
#include "c.h"
|
||||
typedef Node NODEPTR_TYPE;
|
||||
#define OP_LABEL(p) (specific((p)->op))
|
||||
#define LEFT_CHILD(p) ((p)->kids[0])
|
||||
#define RIGHT_CHILD(p) ((p)->kids[1])
|
||||
#define STATE_LABEL(p) ((p)->x.state)
|
||||
#define PANIC error
|
||||
%}
|
||||
%term CNSTF=17 CNSTI=21 CNSTP=23 CNSTU=22
|
||||
%term ARGB=41 ARGF=33 ARGI=37 ARGP=39 ARGU=38
|
||||
%term ASGNB=57 ASGNF=49 ASGNI=53 ASGNP=55 ASGNU=54
|
||||
%term INDIRB=73 INDIRF=65 INDIRI=69 INDIRP=71 INDIRU=70
|
||||
%term CVFF=113 CVFI=117
|
||||
%term CVIF=129 CVII=133 CVIU=134
|
||||
%term CVPP=151 CVPU=150
|
||||
%term CVUI=181 CVUP=183 CVUU=182
|
||||
%term NEGF=193 NEGI=197
|
||||
%term CALLB=217 CALLF=209 CALLI=213 CALLP=215 CALLU=214 CALLV=216
|
||||
%term RETF=241 RETI=245 RETP=247 RETU=246 RETV=248
|
||||
%term ADDRGP=263
|
||||
%term ADDRFP=279
|
||||
%term ADDRLP=295
|
||||
%term ADDF=305 ADDI=309 ADDP=311 ADDU=310
|
||||
%term SUBF=321 SUBI=325 SUBP=327 SUBU=326
|
||||
%term LSHI=341 LSHU=342
|
||||
%term MODI=357 MODU=358
|
||||
%term RSHI=373 RSHU=374
|
||||
%term BANDI=389 BANDU=390
|
||||
%term BCOMI=405 BCOMU=406
|
||||
%term BORI=421 BORU=422
|
||||
%term BXORI=437 BXORU=438
|
||||
%term DIVF=449 DIVI=453 DIVU=454
|
||||
%term MULF=465 MULI=469 MULU=470
|
||||
%term EQF=481 EQI=485 EQU=486
|
||||
%term GEF=497 GEI=501 GEU=502
|
||||
%term GTF=513 GTI=517 GTU=518
|
||||
%term LEF=529 LEI=533 LEU=534
|
||||
%term LTF=545 LTI=549 LTU=550
|
||||
%term NEF=561 NEI=565 NEU=566
|
||||
%term JUMPV=584
|
||||
%term LABELV=600
|
||||
%%
|
||||
stmt: INDIRB(P) ""
|
||||
stmt: INDIRF(P) ""
|
||||
stmt: INDIRI(P) ""
|
||||
stmt: INDIRU(P) ""
|
||||
stmt: INDIRP(P) ""
|
||||
stmt: CALLF(P) ""
|
||||
stmt: CALLI(P) ""
|
||||
stmt: CALLU(P) ""
|
||||
stmt: CALLP(P) ""
|
||||
stmt: V ""
|
||||
bogus: I "" 1
|
||||
bogus: U "" 1
|
||||
bogus: P "" 1
|
||||
bogus: F "" 1
|
||||
bogus: B "" 1
|
||||
bogus: V "" 1
|
||||
I: bogus "" 1
|
||||
U: bogus "" 1
|
||||
P: bogus "" 1
|
||||
F: bogus "" 1
|
||||
B: bogus "" 1
|
||||
V: bogus "" 1
|
||||
F: CNSTF ""
|
||||
I: CNSTI ""
|
||||
P: CNSTP ""
|
||||
U: CNSTU ""
|
||||
V: ARGB(B) ""
|
||||
V: ARGF(F) ""
|
||||
V: ARGI(I) ""
|
||||
V: ARGU(U) ""
|
||||
V: ARGP(P) ""
|
||||
V: ASGNB(P,B) ""
|
||||
V: ASGNF(P,F) ""
|
||||
V: ASGNI(P,I) ""
|
||||
V: ASGNU(P,U) ""
|
||||
V: ASGNP(P,P) ""
|
||||
B: INDIRB(P) ""
|
||||
F: INDIRF(P) ""
|
||||
I: INDIRI(P) ""
|
||||
U: INDIRU(P) ""
|
||||
P: INDIRP(P) ""
|
||||
I: CVII(I) ""
|
||||
I: CVUI(U) ""
|
||||
I: CVFI(F) ""
|
||||
U: CVIU(I) ""
|
||||
U: CVUU(U) ""
|
||||
U: CVPU(P) ""
|
||||
F: CVIF(I) ""
|
||||
F: CVFF(F) ""
|
||||
P: CVUP(U) ""
|
||||
P: CVPP(P) ""
|
||||
F: NEGF(F) ""
|
||||
I: NEGI(I) ""
|
||||
V: CALLB(P,P) ""
|
||||
F: CALLF(P) ""
|
||||
I: CALLI(P) ""
|
||||
U: CALLU(P) ""
|
||||
P: CALLP(P) ""
|
||||
V: CALLV(P) ""
|
||||
V: RETF(F) ""
|
||||
V: RETI(I) ""
|
||||
V: RETU(U) ""
|
||||
V: RETP(P) ""
|
||||
V: RETV ""
|
||||
P: ADDRGP ""
|
||||
P: ADDRFP ""
|
||||
P: ADDRLP ""
|
||||
F: ADDF(F,F) ""
|
||||
I: ADDI(I,I) ""
|
||||
P: ADDP(P,I) ""
|
||||
P: ADDP(I,P) ""
|
||||
P: ADDP(U,P) ""
|
||||
P: ADDP(P,U) ""
|
||||
U: ADDU(U,U) ""
|
||||
F: SUBF(F,F) ""
|
||||
I: SUBI(I,I) ""
|
||||
P: SUBP(P,I) ""
|
||||
P: SUBP(P,U) ""
|
||||
U: SUBU(U,U) ""
|
||||
I: LSHI(I,I) ""
|
||||
U: LSHU(U,I) ""
|
||||
I: MODI(I,I) ""
|
||||
U: MODU(U,U) ""
|
||||
I: RSHI(I,I) ""
|
||||
U: RSHU(U,I) ""
|
||||
U: BANDU(U,U) ""
|
||||
I: BANDI(I,I) ""
|
||||
U: BCOMU(U) ""
|
||||
I: BCOMI(I) ""
|
||||
I: BORI(I,I) ""
|
||||
U: BORU(U,U) ""
|
||||
U: BXORU(U,U) ""
|
||||
I: BXORI(I,I) ""
|
||||
F: DIVF(F,F) ""
|
||||
I: DIVI(I,I) ""
|
||||
U: DIVU(U,U) ""
|
||||
F: MULF(F,F) ""
|
||||
I: MULI(I,I) ""
|
||||
U: MULU(U,U) ""
|
||||
V: EQF(F,F) ""
|
||||
V: EQI(I,I) ""
|
||||
V: EQU(U,U) ""
|
||||
V: GEF(F,F) ""
|
||||
V: GEI(I,I) ""
|
||||
V: GEU(U,U) ""
|
||||
V: GTF(F,F) ""
|
||||
V: GTI(I,I) ""
|
||||
V: GTU(U,U) ""
|
||||
V: LEF(F,F) ""
|
||||
V: LEI(I,I) ""
|
||||
V: LEU(U,U) ""
|
||||
V: LTF(F,F) ""
|
||||
V: LTI(I,I) ""
|
||||
V: LTU(U,U) ""
|
||||
V: NEF(F,F) ""
|
||||
V: NEI(I,I) ""
|
||||
V: NEU(U,U) ""
|
||||
V: JUMPV(P) ""
|
||||
V: LABELV ""
|
||||
%%
|
||||
|
||||
static void reduce(NODEPTR_TYPE p, int goalnt) {
|
||||
int i, sz = opsize(p->op), rulenumber = _rule(p->x.state, goalnt);
|
||||
short *nts = _nts[rulenumber];
|
||||
NODEPTR_TYPE kids[10];
|
||||
|
||||
assert(rulenumber);
|
||||
_kids(p, rulenumber, kids);
|
||||
for (i = 0; nts[i]; i++)
|
||||
reduce(kids[i], nts[i]);
|
||||
switch (optype(p->op)) {
|
||||
#define xx(ty) if (sz == ty->size) return
|
||||
case I:
|
||||
case U:
|
||||
xx(chartype);
|
||||
xx(shorttype);
|
||||
xx(inttype);
|
||||
xx(longtype);
|
||||
xx(longlong);
|
||||
xx(signedptr);
|
||||
xx(unsignedptr);
|
||||
break;
|
||||
case F:
|
||||
xx(floattype);
|
||||
xx(doubletype);
|
||||
xx(longdouble);
|
||||
break;
|
||||
case P:
|
||||
xx(voidptype);
|
||||
xx(funcptype);
|
||||
break;
|
||||
case V:
|
||||
case B: if (sz == 0) return;
|
||||
#undef xx
|
||||
}
|
||||
printdag(p, 2);
|
||||
assert(0);
|
||||
}
|
||||
|
||||
void check(Node p) {
|
||||
struct _state { short cost[1]; };
|
||||
|
||||
_label(p);
|
||||
if (((struct _state *)p->x.state)->cost[1] > 0) {
|
||||
printdag(p, 2);
|
||||
assert(0);
|
||||
}
|
||||
reduce(p, 1);
|
||||
}
|
||||
1174
src/cmd/lccom-1/decl.c
Normal file
1174
src/cmd/lccom-1/decl.c
Normal file
File diff suppressed because it is too large
Load Diff
774
src/cmd/lccom-1/doc/install.html
Normal file
774
src/cmd/lccom-1/doc/install.html
Normal file
@@ -0,0 +1,774 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<link HREF="mailto:drh@cs.princeton.edu" REV="made" TITLE="David R. Hanson">
|
||||
<title>Installing lcc</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<h1>Installing lcc</h1>
|
||||
|
||||
<p ALIGN="LEFT"><strong><a HREF="http://www.research.microsoft.com/~cwfraser/">Christopher
|
||||
W. Fraser</a> and <a HREF="http://www.research.microsoft.com/~drh/">David R. Hanson</a>, <a
|
||||
HREF="http://www.research.microsoft.com/">Microsoft Research</a></strong><br>
|
||||
September 2002</p>
|
||||
|
||||
<h2>Contents</h2>
|
||||
|
||||
<dir>
|
||||
<li><a HREF="#intro">Introduction</a></li>
|
||||
<li><a HREF="#unix">Installation on UNIX</a></li>
|
||||
<li><a HREF="#driver">Building the Driver</a></li>
|
||||
<li><a HREF="#rcc">Building the Compiler and Accessories</a></li>
|
||||
<li><a HREF="#win32">Installation on Windows</a></li>
|
||||
<li><a HREF="#bugs">Reporting Bugs</a></li>
|
||||
<li><a HREF="#mailinglist">Keeping in Touch</a></li>
|
||||
</dir>
|
||||
|
||||
<h2><a NAME="intro">Introduction</a></h2>
|
||||
|
||||
<p><a HREF="http://www.cs.princeton.edu/software/lcc/">lcc</a> is the ANSI C compiler
|
||||
described in our book <cite>A Retargetable C Compiler: Design and Implementation</cite>
|
||||
(Addison-Wesley, 1995, ISBN 0-8053-1670-1).</p>
|
||||
|
||||
<p>If you're installing lcc on a UNIX system, read the remainder of this section and
|
||||
continue with the next section. If you're installing lcc on a Windows system, you should read the rest of this section, the following three sections, and the <a
|
||||
HREF="#win32">Windows</a> section.</p>
|
||||
|
||||
<p>Extract the distribution into its own directory. All non-absolute paths below are
|
||||
relative to this directory. The distribution holds the following subdirectories.</p>
|
||||
|
||||
<blockquote>
|
||||
<table BORDER="0" CELLPADDING="1" CELLSPACING="1" WIDTH="80%">
|
||||
<tr>
|
||||
<td><a HREF="../src"><code>src</code></a></td>
|
||||
<td></td>
|
||||
<td>source code</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a HREF="../etc"><code>etc</code></a></td>
|
||||
<td></td>
|
||||
<td>driver, accessories</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a HREF="../lib"><code>lib</code></a></td>
|
||||
<td></td>
|
||||
<td>runtime library source code</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a HREF="../cpp"><code>cpp</code></a></td>
|
||||
<td></td>
|
||||
<td>preprocessor source code</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a HREF="../lburg"><code>lburg</code></a></td>
|
||||
<td></td>
|
||||
<td>code-generator generator source code</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a HREF="../doc"><code>doc</code></a></td>
|
||||
<td></td>
|
||||
<td>this document, man pages</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code><a HREF="../include">include</a>/*/*</code></td>
|
||||
<td></td>
|
||||
<td>include files</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a HREF="../tst"><code>tst</code></a></td>
|
||||
<td></td>
|
||||
<td>test suite</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code><a HREF="../alpha">alpha</a>/*/tst</code></td>
|
||||
<td></td>
|
||||
<td>ALPHA test outputs</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code><a HREF="../mips">mips</a>/*/tst</code></td>
|
||||
<td></td>
|
||||
<td>MIPS test outputs</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code><a HREF="../sparc">sparc</a>/*/tst</code></td>
|
||||
<td></td>
|
||||
<td>SPARC test outputs</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code><a HREF="../x86">x86</a>/*/tst</code></td>
|
||||
<td></td>
|
||||
<td>X86 test outputs</td>
|
||||
</tr>
|
||||
</table>
|
||||
</blockquote>
|
||||
|
||||
<p><code>doc/install.html</code> is the HTML file for this document.</p>
|
||||
|
||||
<p>The installation makefile is designed so that lcc can be installed from a read-only
|
||||
file system or directory, which is common in networked environments, so the distribution
|
||||
can be unloaded on a central file server. <strong>You will need an existing ANSI/ISO C
|
||||
compiler to build and install lcc.</strong></p>
|
||||
|
||||
<h2><a NAME="unix">Installation on UNIX</a></h2>
|
||||
|
||||
<p>The compilation components (the preprocessor, include files, and compiler proper, etc.)
|
||||
are installed in a single <em>build directory</em>. On multi-platform systems supported by
|
||||
a central file server, it's common to store the build directory in a location specific to
|
||||
the platform and to the version of lcc, and to point a symbolic link to this location. For
|
||||
example,</p>
|
||||
|
||||
<blockquote>
|
||||
<pre>% ln -s $BUILDDIR/sparc-solaris /usr/local/lib/lcc</pre>
|
||||
</blockquote>
|
||||
|
||||
<p>points <code>/usr/local/lib/lcc</code> to a build directory for lcc on the SPARC under Solaris. Links into <code>/usr/local/lib/lcc</code> are created for the programs <code>lcc</code>
|
||||
and <code>bprint</code>. Thus, a new distribution can be installed by building it in its
|
||||
own build directory and changing one symbolic link to point to that directory. If these
|
||||
conventions or their equivalents are followed, the host-specific parts of the driver
|
||||
program, <code>lcc</code>, can be used unmodified.</p>
|
||||
|
||||
<p>Installation on a UNIX system involves the following steps. Below, the build directory
|
||||
is referred to as <code>BUILDDIR</code>, and the commands below are executed
|
||||
from the distribution directory.<ol>
|
||||
<li>Create the build directory, using a version- and platform-specific naming convention as
|
||||
suggested above, and record the name of this directory in the <code>BUILDDIR</code>
|
||||
environment variable:<blockquote>
|
||||
<pre>% setenv BUILDDIR $BUILDDIR/sparc-solaris
|
||||
% mkdir -p $BUILDDIR</pre>
|
||||
</blockquote>
|
||||
<p>Here and below, commands assume the C shell. Also, you'll need a version of <code>mkdir</code>
|
||||
that supports the <code>-p</code> option, which creates intermediate directories as
|
||||
necessary.</p>
|
||||
</li>
|
||||
<li>Copy the man pages to the repository for local man pages, e.g.,<blockquote>
|
||||
<pre>% cp doc/*.1 /usr/local/man/man1</pre>
|
||||
</blockquote>
|
||||
<p>Some users copy the man pages to the build directory and create the appropriate
|
||||
symbolic links, e.g., </p>
|
||||
<blockquote>
|
||||
<pre>% cp doc/*.1 $BUILDDIR
|
||||
% ln -s $BUILDDIR/*.1 /usr/local/man/man1</pre>
|
||||
</blockquote>
|
||||
</li>
|
||||
<li>Platform-specific include files are in directories named <code>include/</code><em>target</em><code>/</code><em>os</em>.
|
||||
Create the include directory in the build directory, and copy the include hierarchy for
|
||||
your platform to this directory, e.g.,<blockquote>
|
||||
<pre>% mkdir $BUILDDIR/include
|
||||
% cp -p -R include/sparc/solaris/* $BUILDDIR/include</pre>
|
||||
</blockquote>
|
||||
<p>Again, some users create a symbolic link to the appropriate directory in the
|
||||
distribution instead of copying the include files. For example, at Princeton, the
|
||||
distributions are stored under <code>/proj/pkg/lcc</code>, so the included files are
|
||||
"installed" by creating one symbolic link: </p>
|
||||
<blockquote>
|
||||
<pre>% ln -s $BUILDDIR/include/sparc/solaris $BUILDDIR/include</pre>
|
||||
</blockquote>
|
||||
<p>If you're installing lcc on Linux, you <em>must</em> also plant a symbolic link named <code>gcc</code>
|
||||
to gcc's library directory, because lcc uses gcc's C preprocessor and most of gcc's header
|
||||
files:</p>
|
||||
<blockquote>
|
||||
<pre>% ln -s /usr/lib/gcc-lib/i386-redhat-linux/2.96 $BUILDDIR/gcc</pre>
|
||||
</blockquote>
|
||||
<p>The library directory shown above may be different on your Linux machine; to determine
|
||||
the correct directory, browse <code>/usr/lib/gcc-lib</code>, or execute</p>
|
||||
<blockquote>
|
||||
<pre>% cc -v tst/8q.c</pre>
|
||||
</blockquote>
|
||||
<p>and examine the diagnostic output. Make sure that <code>$BUILDDIR/gcc/cpp0</code> and <code>$BUILDDIR/gcc/include</code>
|
||||
are, respectively, gcc's C preprocessor and header files. On Linux, lcc looks for
|
||||
include files in <code>$BUILDDIR/include</code>, <code>$BUILDDIR/gcc/include</code>, and <code>/usr/include</code>,
|
||||
in that order; see <a HREF="#driver"><em>Building the Driver</em></a> and <a
|
||||
href="../etc/linux.c"><code>etc/linux.c</code></a> for details.</p>
|
||||
</li>
|
||||
<li>The <a HREF="../makefile"><code>makefile</code></a> includes the file named by the <code>CUSTOM</code>
|
||||
macro; the default is <code>custom.mk</code>, and an empty <code>custom.mk</code> is
|
||||
included in the distribution. If desired, prepare a site-specification customization file
|
||||
and define <code>CUSTOM</code> to the path of that file when invoking make in steps 5 and
|
||||
6, e.g.,<blockquote>
|
||||
<pre>make CUSTOM=<a href="http://www.cs.princeton.edu/software/lcc/pkg/solaris.mk">solaris.mk</a></pre>
|
||||
</blockquote>
|
||||
<p>You can, for example, use customization files to record site-specific values for macros
|
||||
instead of using environment variables, and to record targets for the steps in this list.</p>
|
||||
</li>
|
||||
<li>Build the host-specific driver, creating a custom host-specific part, if necessary. See <a
|
||||
HREF="#driver"><em>Building the Driver</em></a>.</li>
|
||||
<li>Build the preprocessor, compiler proper, library, and other accessories. See <a
|
||||
HREF="#rcc"><em>Building the Compiler</em></a>.</li>
|
||||
<li>Plant symbolic links to the build directory and to the installed programs, e.g.,<blockquote>
|
||||
<pre>% ln -s $BUILDDIR /usr/local/lib/lcc
|
||||
% ln -s /usr/local/lib/{lcc,bprint} /usr/local/bin</pre>
|
||||
</blockquote>
|
||||
<p>Some users copy <code>bprint</code> and <code>lcc</code> into <code>/usr/local/bin</code>
|
||||
instead of creating symbolic links. The advantage of creating the links for <code>lcc</code>
|
||||
and <code>bprint</code> as shown is that, once established, they point indirectly to
|
||||
whatever <code>/usr/local/lib/lcc</code> points to; installing a new version of lcc can be done by changing <code>/usr/local/lib/lcc</code> to point to the build
|
||||
directory for the new version.</p>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<h2><a NAME="driver">Building the Driver</a></h2>
|
||||
|
||||
<p>The preprocessor, compiler, assembler, and loader are invoked by a driver program, <code>lcc</code>,
|
||||
which is similar to <code>cc</code> on most systems. It's described in the man page <code>doc/lcc.1</code>.
|
||||
The driver is built by combining the host-independent part, <a href="../etc/lcc.c"><code>etc/lcc.c</code></a>,
|
||||
with a small host-specific part. Distributed host-specific parts are named <code>etc/</code><em>os</em><code>.c</code>,
|
||||
where <em>os</em> is the name of the operating system for the host on which <code>lcc</code>
|
||||
is being installed. If you're following the installations conventions described above, you
|
||||
can probably use one of the host-specific parts unmodified; otherwise, pick one that is
|
||||
closely related to your platform, copy it to <em>whatever</em><code>.c</code>, and edit it
|
||||
as described below. You should not have to edit <code>etc/lcc.c</code>.</p>
|
||||
|
||||
<p>We'll use <a HREF="../etc/solaris.c"><code>etc/solaris.c</code></a> as an example in
|
||||
describing how the host-specific part works. This example illustrates all the important
|
||||
features. Make sure you have the environment variable <code>BUILDDIR</code> set correctly,
|
||||
and build the driver with a <code>make</code> command, e.g.,</p>
|
||||
|
||||
<blockquote>
|
||||
<pre>% make HOSTFILE=etc/solaris.c lcc
|
||||
cc -g -c -o $BUILDDIR/sparc-solaris/lcc.o etc/lcc.c
|
||||
cc -g -c -o $BUILDDIR/sparc-solaris/host.o etc/solaris.c
|
||||
cc -g -o $BUILDDIR/lcc $BUILDDIR/sparc-solaris/lcc.o $BUILDDIR/sparc-solaris/host.o</pre>
|
||||
</blockquote>
|
||||
|
||||
<p>Of course, the actual value of <code>BUILDDIR</code> will appear in place of
|
||||
<code>$BUILDDIR</code>. The symbolic name <code>HOSTFILE</code> specifies the path to the host-specific part,
|
||||
either one in the distribution or <em>whatever</em><code>.c</code>. Some versions of make
|
||||
may require the <code>-e</code> option in order to read the environment.</p>
|
||||
|
||||
<p>Here's <code>etc/solaris.c</code>:</p>
|
||||
|
||||
<blockquote>
|
||||
<pre>/* Sparcs running Solaris 2.5.1 at CS Dept., Princeton University */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static char rcsid[] = "$ Id: solaris.c,v 1.10 1998/09/14 20:36:33 drh Exp $";
|
||||
|
||||
#ifndef LCCDIR
|
||||
#define LCCDIR "/usr/local/lib/lcc/"
|
||||
#endif
|
||||
#ifndef SUNDIR
|
||||
#define SUNDIR "/opt/SUNWspro/SC4.2/lib/"
|
||||
#endif
|
||||
|
||||
char *suffixes[] = { ".c", ".i", ".s", ".o", ".out", 0 };
|
||||
char inputs[256] = "";
|
||||
char *cpp[] = { LCCDIR "cpp",
|
||||
"-D__STDC__=1", "-Dsparc", "-D__sparc__", "-Dsun", "-D__sun__", "-Dunix",
|
||||
"$1", "$2", "$3", 0 };
|
||||
char *include[] = { "-I" LCCDIR "include", "-I/usr/local/include",
|
||||
"-I/usr/include", 0 };
|
||||
char *com[] = { LCCDIR "rcc", "-target=sparc/solaris",
|
||||
"$1", "$2", "$3", 0 };
|
||||
char *as[] = { "/usr/ccs/bin/as", "-Qy", "-s", "-o", "$3", "$1", "$2", 0 };
|
||||
char *ld[] = { "/usr/ccs/bin/ld", "-o", "$3", "$1",
|
||||
SUNDIR "crti.o", SUNDIR "crt1.o",
|
||||
SUNDIR "values-xa.o", "$2", "",
|
||||
"-Y", "P," SUNDIR ":/usr/ccs/lib:/usr/lib", "-Qy",
|
||||
"-L" LCCDIR, "-llcc", "-lm", "-lc", SUNDIR "crtn.o", 0 };
|
||||
|
||||
extern char *concat(char *, char *);
|
||||
|
||||
int option(char *arg) {
|
||||
if (strncmp(arg, "-lccdir=", 8) == 0) {
|
||||
cpp[0] = concat(&arg[8], "/cpp");
|
||||
include[0] = concat("-I", concat(&arg[8], "/include"));
|
||||
ld[12] = concat("-L", &arg[8]);
|
||||
com[0] = concat(&arg[8], "/rcc");
|
||||
} else if (strcmp(arg, "-p") == 0) {
|
||||
ld[5] = SUNDIR "mcrt1.o";
|
||||
ld[10] = "P," SUNDIR "libp:/usr/ccs/lib/libp:/usr/lib/libp:"
|
||||
SUNDIR ":/usr/ccs/lib:/usr/lib";
|
||||
} else if (strcmp(arg, "-b") == 0)
|
||||
;
|
||||
else if (strncmp(arg, "-ld=", 4) == 0)
|
||||
ld[0] = &arg[4];
|
||||
else
|
||||
return 0;
|
||||
return 1;
|
||||
}</pre>
|
||||
</blockquote>
|
||||
|
||||
<p><code>LCCDIR</code> defaults to <code>"/usr/local/lib/lcc/"</code> unless
|
||||
it's defined by a <code>-D</code> option as part of <code>CFLAGS</code> in the make
|
||||
command, e.g.,</p>
|
||||
|
||||
<blockquote>
|
||||
<pre>% make HOSTFILE=etc/solaris.c CFLAGS='-DLCCDIR=\"/v/lib/lcc/\"' lcc</pre>
|
||||
</blockquote>
|
||||
|
||||
<p>Note the trailing slash; <code>SUNDIR</code> is provided so you can use <code>etc/solaris.c</code>
|
||||
even if you have a different version of the Sun Pro compiler suite. If you're using the
|
||||
gcc compiler tools instead of the Sun Pro tools, see <a HREF="../etc/gcc-solaris.c"><code>etc/gcc-solaris.c</code></a>.</p>
|
||||
|
||||
<p>Most of the host-specific code is platform-specific data and templates for the commands
|
||||
that invoke the preprocessor, compiler, assembler, and loader. The <code>suffixes</code>
|
||||
array lists the file name suffixes for C source files, preprocessed source files, assembly
|
||||
language source files, object files, and executable files. <code>suffixes</code> must be
|
||||
terminated with a null pointer, as shown above. The initialization of <code>suffixes</code>
|
||||
in <code><a HREF="../etc/solaris.c">etc/solaris.c</a></code> are the typical ones for UNIX
|
||||
systems. Each element of <code>suffixes</code> is actually a list of suffixes, separated
|
||||
by semicolons; <code><a HREF="../etc/win32.c">etc/win32.c</a></code> holds an example:</p>
|
||||
|
||||
<blockquote>
|
||||
<pre>char *suffixes[] = { ".c;.C", ".i;.I", ".asm;.ASM;.s;.S", ".obj;.OBJ", ".exe", 0 };</pre>
|
||||
</blockquote>
|
||||
|
||||
<p>When a list is given, the first suffix is used whenever lcc needs to generate a file
|
||||
name. For example, with <code><a HREF="../etc/win32.c">etc/win32.c</a></code>, lcc emits
|
||||
the generated assembly code into <code>.asm</code> files.</p>
|
||||
|
||||
<p>The <code>inputs</code> array holds a null-terminated string of directories separated
|
||||
by colons or semicolons. These are used as the default value of <code>LCCINPUTS</code>, if
|
||||
the environment variable <code>LCCINPUTS</code> is not set; see the <a HREF="lcc.pdf">man
|
||||
page</a>.</p>
|
||||
|
||||
<p>Each command template is an array of pointers to strings terminated with a null
|
||||
pointer; the strings are full path names of commands, arguments, or argument placeholders,
|
||||
which are described below. Commands are executed in a child process, and templates can
|
||||
contain multiple commands by separating commands with newlines. The driver runs each
|
||||
command in a new process.</p>
|
||||
|
||||
<p>The <code>cpp</code> array gives the command for running lcc's preprocessor, <code>cpp</code>.
|
||||
Literal arguments specified in templates, e.g., <code>"-Dsparc"</code> in the <code>cpp</code>
|
||||
command above, are passed to the command as given.</p>
|
||||
|
||||
<p>The strings <code>"$1"</code>, <code>"$2"</code>, and <code>"$3"</code>
|
||||
in templates are placeholders for <em>lists</em> of arguments that are substituted in a
|
||||
copy of the template before the command is executed. <code>$1</code> is replaced by the <em>options</em>
|
||||
specified by the user; for the preprocessor, this list always contains at least <code>-D__LCC__</code>.
|
||||
<code>$2</code> is replaced by the <em>input</em> files, and <code>$3</code> is replaced
|
||||
by the <em>output</em> file.</p>
|
||||
|
||||
<p>Zero-length arguments after replacement are removed from the argument list before the
|
||||
command is invoked. So, for example, if the preprocessor is invoked without an output
|
||||
file, <code>"$3"</code> becomes <code>""</code>, which is removed from
|
||||
the final argument list.</p>
|
||||
|
||||
<p>The <code>include</code> array is a list of <code>-I</code> options that specify which
|
||||
directives should be searched to satisfy include directives. These directories are
|
||||
searched in the order given. The first directory should be the one to which the ANSI
|
||||
header files were copied as described in <a HREF="#unix">UNIX</a> or <a HREF="#win32">Windows</a>
|
||||
installation instructions. The driver adds these options to <code>cpp</code>'s arguments
|
||||
when it invokes the preprocessor, except when <code>-N</code> is specified.</p>
|
||||
|
||||
<p><code>com</code> gives the command for invoking the compiler. This template can appear
|
||||
as shown above in a custom host-specific part, but the option <code>-target=sparc/solaris</code>
|
||||
should be edited to the <em>target</em><code>/</code><em>os</em> for your platform. If <code>com[1]</code>
|
||||
includes the string "<code>win32</code>", the driver assumes it's running on
|
||||
Windows. lcc can generate code for <em>all</em> of the <em>target</em><code>/</code><em>os</em>
|
||||
combinations listed in the file <code>src/bind.c</code>. The <code>-target</code> option
|
||||
specifies the default combination. The driver's <code>-Wf</code> option can be used to
|
||||
specify other combinations; the <a HREF="lcc.pdf">man page</a> elaborates.</p>
|
||||
|
||||
<p><code>as</code> gives the command for invoking the assembler. On Linux, you must be
|
||||
running at least version 2.8.1 of the GNU assembler; earlier versions mis-assemble some
|
||||
instructions emitted by lcc.</p>
|
||||
|
||||
<p><code>ld</code> gives the command for invoking the loader. For the other commands, the
|
||||
list <code>$2</code> contains a single file; for <code>ld</code>, <code>$2</code> contains
|
||||
all ".o" files and libraries, and <code>$3</code> is <code>a.out</code>, unless
|
||||
the <code>-o</code> option is specified. As suggested in the code above, <code>ld</code>
|
||||
must also specify the appropriate startup code and default libraries, including the lcc
|
||||
library, <code>liblcc.a</code>.</p>
|
||||
|
||||
<p>The <code>option</code> function is described below; the minimal <code>option</code>
|
||||
function just returns 0.</p>
|
||||
|
||||
<p>You can test <code>lcc</code> with the options <code>-v -v</code> to display the
|
||||
commands that would be executed, e.g.,</p>
|
||||
|
||||
<blockquote>
|
||||
<pre>% $BUILDDIR/lcc -v -v foo.c baz.c mylib.a -lX11
|
||||
$BUILDDIR/sparc-solaris/lcc $ Id: lcc.c,v 4.33 2001/06/28 22:19:58 drh $
|
||||
foo.c:
|
||||
/usr/local/lib/lcc/cpp -D__STDC__=1 -Dsparc -D__sparc__ -Dsun -D__sun__ -Dunix -D__LCC__i
|
||||
/usr/local/lib/lcc/rcc -target=sparc/solaris -v /tmp/lcc4060.i /tmp/lcc4061.s
|
||||
/usr/ccs/bin/as -Qy -s -o /tmp/lcc4062.o /tmp/lcc4061.s
|
||||
baz.c:
|
||||
/usr/local/lib/lcc/cpp -D__STDC__=1 -Dsparc -D__sparc__ -Dsun -D__sun__ -Dunix -D__LCC__i
|
||||
/usr/local/lib/lcc/rcc -target=sparc/solaris -v /tmp/lcc4060.i /tmp/lcc4061.s
|
||||
/usr/ccs/bin/as -Qy -s -o /tmp/lcc4063.o /tmp/lcc4061.s
|
||||
/usr/ccs/bin/ld -o a.out /opt/SUNWspro/SC4.2/lib/crti.o /opt/SUNWspro/SC4.2/lib/crt1.o /o
|
||||
rm /tmp/lcc4063.o /tmp/lcc4060.i /tmp/lcc4061.s /tmp/lcc4062.o</pre>
|
||||
</blockquote>
|
||||
|
||||
<p>As the output shows, <code>lcc</code> places temporary files in <code>/tmp</code>; if
|
||||
any of the environment variables <code>TMP</code>, <code>TEMP</code>, and <code>TMPDIR</code>
|
||||
are set, they override this default (in the order shown) as does the <code>-tempdir=</code><em>dir</em>
|
||||
option. The default can be changed by defining <code>TEMPDIR</code> in <code>CFLAGS</code>
|
||||
when building the driver.</p>
|
||||
|
||||
<p>The <code>option</code> function is called for the options <code>-Wo</code>, <code>-g</code>,
|
||||
<code>-p</code>, <code>-pg</code>, and <code>-b</code> because these compiler options
|
||||
might also affect the loader's arguments. For these options, the driver calls <code>option(arg)</code>
|
||||
to give the host-specific code an opportunity to edit the <code>ld</code> command, if
|
||||
necessary. <code>option</code> can change <code>ld</code>, if necessary, and return 1 to
|
||||
announce its acceptance of the option. If the option is unsupported, <code>option</code>
|
||||
should return 0.</p>
|
||||
|
||||
<p>For example, in response to <code>-g</code>, the <code>option</code> function shown
|
||||
above accepts the option but does nothing else, because the <code>ld</code> and <code>as</code>
|
||||
commands don't need to be modified on the SPARC. <code>-g</code> will also be added to the
|
||||
compiler's options by the host-independent part of the driver. The <code>-p</code> causes <code>option</code>
|
||||
to change the name of the startup code and changed the list of libraries. The <code>-b</code>
|
||||
option turns on <code>lcc</code>'s per-expression profiling, the code for which is in <code>liblcc.a</code>,
|
||||
so <code>option</code> need no nothing.</p>
|
||||
|
||||
<p>On SPARCs, the driver also recognizes <code>-Bstatic</code> and <code>-Bdynamic</code>
|
||||
as linker options. The driver recognizes but ignores "<code>-target</code> <em>name</em>"
|
||||
option.</p>
|
||||
|
||||
<p>The option <code>-Wo</code><em>arg</em> causes the driver to pass <em>arg</em> to <code>option</code>.
|
||||
Such options have no other effect; this mechanism is provided to support system-specific
|
||||
options that affect the commands executed by the driver. As illustrated above,
|
||||
host-specific parts should support the <code>-Wo-lccdir=</code><em>dir</em> option, which
|
||||
causes lcc's compilation components to be found in <em>dir</em>, because this option is
|
||||
used by the test scripts, and because the driver simulates a <code>-Wo-lccdir</code>
|
||||
option with the value of the environment variable <code>LCCDIR</code>, if it's defined.
|
||||
The code above rebuilds the paths to the include files, preprocessor, compiler, and
|
||||
library by calling <code>concat</code>, which is defined in <code>etc/lcc.c</code>.</p>
|
||||
|
||||
<h2><a NAME="rcc">Building the Compiler and Accessories</a></h2>
|
||||
|
||||
<p>To build the rest of compilation components make sure <code>BUILDDIR</code> is set
|
||||
appropriately and type "<code>make all</code>". This command builds <code>librcc.a</code>
|
||||
(the compiler's private library), <code>rcc</code> (the compiler proper), <code>lburg</code>
|
||||
(the code-generator generator), <code>cpp</code> (the preprocessor), <code>liblcc.a</code>
|
||||
(the runtime library), and <code>bprint</code> (the profile printer), all in <code>BUILDDIR</code>.
|
||||
There may be warnings, but there should be no errors. If you're using an ANSI/ISO compiler
|
||||
other than <code>cc</code>, specify its name with the <code>CC=</code> option, e.g.,
|
||||
"<code>make CC=gcc all</code>". If you're running on a DEC ALPHA, use "<code>make
|
||||
CC='cc -std1' all</code>"; the <code>-std1</code> option is essential on
|
||||
the ALPHA.</p>
|
||||
|
||||
<p>Once <code>rcc</code> is built with the host C compiler, run the test suite to verify
|
||||
that <code>rcc</code> is working correctly. If any of the steps below fail, contact us
|
||||
(see <a HREF="#bugs"><em>Reporting Bugs</em></a>). The commands in the makefile run the
|
||||
shell script <code>src/run.sh</code> on each C program in the test suite, <code>tst/*.c</code>.
|
||||
It uses the driver, <code>$BUILDDIR/lcc</code>, so you must have the driver in the build
|
||||
directory before testing <code>rcc</code>. The <em>target</em><code>/</code><em>os</em>
|
||||
combination is read from the variable <code>TARGET</code>, which must be specified when
|
||||
invoking <code>make</code>:</p>
|
||||
|
||||
<blockquote>
|
||||
<pre>% make TARGET=sparc/solaris test
|
||||
mkdir -p $BUILDDIR/sparc-solaris/sparc/solaris/tst
|
||||
$BUILDDIR/sparc-solaris/rcc -target=sparc/solaris $BUILDDIR/sparc-solaris/sparc/solaris/tst/8q.s:
|
||||
$BUILDDIR/sparc-solaris/rcc -target=sparc/solaris $BUILDDIR/sparc-solaris/sparc/solaris/tst/array.s:
|
||||
$BUILDDIR/sparc-solaris/rcc -target=sparc/solaris $BUILDDIR/sparc-solaris/sparc/solaris/tst/cf.s:
|
||||
$BUILDDIR/sparc-solaris/rcc -target=sparc/solaris $BUILDDIR/sparc-solaris/sparc/solaris/tst/cq.s:
|
||||
$BUILDDIR/sparc-solaris/rcc -target=sparc/solaris $BUILDDIR/sparc-solaris/sparc/solaris/tst/cvt.s:
|
||||
$BUILDDIR/sparc-solaris/rcc -target=sparc/solaris $BUILDDIR/sparc-solaris/sparc/solaris/tst/fields.s:
|
||||
$BUILDDIR/sparc-solaris/rcc -target=sparc/solaris $BUILDDIR/sparc-solaris/sparc/solaris/tst/front.s:
|
||||
$BUILDDIR/sparc-solaris/rcc -target=sparc/solaris $BUILDDIR/sparc-solaris/sparc/solaris/tst/incr.s:
|
||||
$BUILDDIR/sparc-solaris/rcc -target=sparc/solaris $BUILDDIR/sparc-solaris/sparc/solaris/tst/init.s:
|
||||
$BUILDDIR/sparc-solaris/rcc -target=sparc/solaris $BUILDDIR/sparc-solaris/sparc/solaris/tst/limits.s:
|
||||
$BUILDDIR/sparc-solaris/rcc -target=sparc/solaris $BUILDDIR/sparc-solaris/sparc/solaris/tst/paranoia.s:
|
||||
$BUILDDIR/sparc-solaris/rcc -target=sparc/solaris $BUILDDIR/sparc-solaris/sparc/solaris/tst/sort.s:
|
||||
$BUILDDIR/sparc-solaris/rcc -target=sparc/solaris $BUILDDIR/sparc-solaris/sparc/solaris/tst/spill.s:
|
||||
$BUILDDIR/sparc-solaris/rcc -target=sparc/solaris $BUILDDIR/sparc-solaris/sparc/solaris/tst/stdarg.s:
|
||||
$BUILDDIR/sparc-solaris/rcc -target=sparc/solaris $BUILDDIR/sparc-solaris/sparc/solaris/tst/struct.s:
|
||||
$BUILDDIR/sparc-solaris/rcc -target=sparc/solaris $BUILDDIR/sparc-solaris/sparc/solaris/tst/switch.s:
|
||||
$BUILDDIR/sparc-solaris/rcc -target=sparc/solaris $BUILDDIR/sparc-solaris/sparc/solaris/tst/wf1.s:
|
||||
$BUILDDIR/sparc-solaris/rcc -target=sparc/solaris $BUILDDIR/sparc-solaris/sparc/solaris/tst/yacc.s:</pre>
|
||||
</blockquote>
|
||||
|
||||
<p>Each line in the output above is of the form</p>
|
||||
|
||||
<blockquote>
|
||||
<p><code>$BUILDDIR/rcc -target=</code><em>target</em><code>/</code><em>os</em><code> $BUILDDIR/</code><em>target</em><code>/</code><em>os</em><code>/</code><em>X</em><code>.s:</code></p>
|
||||
</blockquote>
|
||||
|
||||
<p>where <em>X</em> is the base name of the C program <em>X</em><code>.c</code> in the
|
||||
test suite. The actual value of <code>BUILDDIR</code> will, of course, appear in
|
||||
place of <code>$BUILDDIR</code>. This output identifies the compiler and the target, e.g., "<code>$BUILDDIR/rcc</code>
|
||||
is generating code for a <code>sparc</code> running the <code>solaris</code> operating
|
||||
system."</p>
|
||||
|
||||
<p>For each program in the test suite, <code>src/run.sh</code> compiles the program, drops
|
||||
the generated assembly language code in <code>BUILDDIR</code>/<em>target</em><code>/</code><em>os</em>,
|
||||
and uses <code>diff</code> to compare the generated assembly code with the expected code
|
||||
(the code expected for <code>tst/8q.c</code> on the SPARC under Solaris is in <code>sparc/solaris/tst/8q.sbk</code>,
|
||||
etc.). If there are differences, the script executes the generated code with the input
|
||||
given in <code>tst</code> (the input for <code>tst/8q.c</code> is in <code>tst/8q.0</code>,
|
||||
etc.) and compares the output with the expected output (the expected output from <code>tst/8q.c</code>
|
||||
on the SPARC under Solaris is in <code>sparc/solaris/tst/8q.1bk</code>, etc.). The script
|
||||
also compares the diagnostics from the compiler with the expected diagnostics.</p>
|
||||
|
||||
<p>On some systems, there may be a few differences between the generated code and the
|
||||
expected code. These differences occur because the expected code is generated by cross
|
||||
compilation and the least significant bits of some floating-point constants differ from
|
||||
those bits in constants generated on your system. On Linux, there may be differences
|
||||
because of differences in the header files between our system and yours. There should be
|
||||
no differences in the output from executing the test programs.</p>
|
||||
|
||||
<p>Next, run the "triple test", which builds <code>rcc</code> using itself:</p>
|
||||
|
||||
<blockquote>
|
||||
<pre>% make triple
|
||||
$BUILDDIR/sparc-solaris/lcc -A -d0.6 -Wo-lccdir=$(BUILDDIR) -Isrc -I$(BUILDDIR) -o $BUILDDIR/sparc-solaris/1rcc -B$BUILDDIR/sparc-solaris/ src/alloc.c ...
|
||||
src/alloc.c:
|
||||
...
|
||||
$BUILDDIR/sparc-solaris/lcc -A -d0.6 -Wo-lccdir=$(BUILDDIR) -Isrc -I$(BUILDDIR) -o $BUILDDIR/sparc-solaris/2rcc -B$BUILDDIR/sparc-solaris/1 src/alloc.c ...
|
||||
src/alloc.c:
|
||||
...
|
||||
strip $BUILDDIR/sparc-solaris/[12]rcc
|
||||
dd if=$BUILDDIR/sparc-solaris/1rcc of=$BUILDDIR/sparc-solaris/rcc1 bs=512 skip=1
|
||||
1270+1 records in
|
||||
1270+1 records out
|
||||
dd if=$BUILDDIR/sparc-solaris/2rcc of=$BUILDDIR/sparc-solaris/rcc2 bs=512 skip=1
|
||||
1270+1 records in
|
||||
1270+1 records out
|
||||
if cmp $BUILDDIR/sparc-solaris/rcc[12]; then \
|
||||
mv $BUILDDIR/sparc-solaris/2rcc $BUILDDIR/sparc-solaris/rcc; \
|
||||
rm -f $BUILDDIR/sparc-solaris/1rcc $BUILDDIR/sparc-solaris/rcc[12]; fi</pre>
|
||||
</blockquote>
|
||||
|
||||
<p>This command builds <code>rcc</code> twice; once using the <code>rcc</code> built by <code>cc</code>
|
||||
and again using the <code>rcc</code> built by <code>lcc</code>. The resulting binaries are
|
||||
compared. They should be identical, as shown at the end of the output above. If they
|
||||
aren't, our compiler is generating incorrect code; <a HREF="#bugs">contact</a> us.</p>
|
||||
|
||||
<p>The final version of <code>rcc</code> should also pass the test suite; that is, the
|
||||
output from</p>
|
||||
|
||||
<blockquote>
|
||||
<pre>% make TARGET=sparc/solaris test</pre>
|
||||
</blockquote>
|
||||
|
||||
<p>should be identical to that from the previous <code>make test</code>.</p>
|
||||
|
||||
<p>The command "<code>make clean</code>" cleans up, but does not remove <code>rcc</code>,
|
||||
etc., and "<code>make clobber</code>" cleans up and removes <code>lcc</code>, <code>rcc</code>,
|
||||
and the other accessories. Test directories under <code>BUILDDIR</code> are <em>not</em>
|
||||
removed; you'll need to remove these by hand, e.g.,</p>
|
||||
|
||||
<blockquote>
|
||||
<pre>% rm -fr $BUILDDIR/sparc</pre>
|
||||
</blockquote>
|
||||
|
||||
<p>The code generators for the other targets can be tested by specifying the desired <em>target</em><code>/</code><em>os</em>
|
||||
and setting an environment variable that controls what <code>src/run.sh</code> does. For
|
||||
example, to test the MIPS code generator, type</p>
|
||||
|
||||
<blockquote>
|
||||
<pre>% setenv REMOTEHOST noexecute
|
||||
% make TARGET=mips/irix test</pre>
|
||||
</blockquote>
|
||||
|
||||
<p>As above, <code>src/run.sh</code> compares the MIPS code generated with what's
|
||||
expected. There should be no differences. Setting <code>REMOTEHOST</code> to <code>noexecute</code>
|
||||
suppresses the assembly and execution of the generated code. If you set <code>REMOTEHOST</code>
|
||||
to the name of a MIPS machine to which you can <code>rlogin</code>, <code>src/run.sh</code>
|
||||
will <code>rcp</code> the generated code to that machine and execute it there, if
|
||||
necessary. See <code>src/run.sh</code> for the details.</p>
|
||||
|
||||
<p>You can use lcc as a cross compiler. The options <code>-S</code> and <code>-Wf-target=</code><em>target/os</em>
|
||||
generate assembly code for the specified target, which is any of those listed in the file <code>src/bind.c</code>.
|
||||
For example, </p>
|
||||
|
||||
<blockquote>
|
||||
<pre>% lcc -Wf-target=mips/irix -S tst/8q.c</pre>
|
||||
</blockquote>
|
||||
|
||||
<p>generates MIPS code for <code>tst/8q.c</code> in <code>8q.s</code>.</p>
|
||||
|
||||
<p>lcc can also generate code for a "symbolic" target. This target is used
|
||||
routinely in front-end development, and its output is a printable representation of the
|
||||
input program, e.g., the dags constructed by the front end are printed, and other
|
||||
interface functions print their arguments. You can specify this target with the option <code>-Wf-target=symbolic</code>.
|
||||
For example,</p>
|
||||
|
||||
<blockquote>
|
||||
<pre>% lcc -Wf-target=symbolic -S tst/8q.c</pre>
|
||||
</blockquote>
|
||||
|
||||
<p>generates symbolic output for <code>tst/8q.c</code> in <code>8q.s</code>. Adding <code>-Wf-html</code>
|
||||
causes the symbolic target to emit HTML instead of plain text. Finally, the option <code>-Wf-target=null</code>
|
||||
specifies the "null" target for which lcc emits nothing and thus only checks the
|
||||
syntax and semantics of its input files.</p>
|
||||
|
||||
<h2><a NAME="win32">Installation on Windows</a></h2>
|
||||
|
||||
<p>On Windows, lcc is designed to work with Microsoft's Visual
|
||||
C++ (VC), version 5.0 and above, and Microsoft's Assembler, MASM. It uses the VC header files,
|
||||
libraries, and command-line tools, and it uses MASM to assemble the code it generates.
|
||||
You must use MASM 6.11d or later,
|
||||
because earlier releases generate incorrect COFF object files. MASM
|
||||
6.15 is available as part of the free
|
||||
<a href="http://msdn.microsoft.com/vstudio/downloads/ppack/default.asp">Visual
|
||||
C++ 6.0 Processor Pack</a>.</p>
|
||||
|
||||
<p>Building the distribution components from the ground up requires Microsoft's Visual
|
||||
C/C++ compiler, Microsoft's make, <code>nmake</code>, and the standard Windows command
|
||||
interpreter. <a HREF="../makefile.nt"><code>makefile.nt</code></a> is written to use only <code>nmake</code>.
|
||||
As on UNIX systems, the compilation components are installed in a single <em>build
|
||||
directory</em>, and the top-level programs, <code>lcc.exe</code> and <code>bprint.exe</code>,
|
||||
are installed in a directory on the PATH. If the conventions used below are followed, the
|
||||
Windows-specific parts of the driver program, <code>lcc.exe</code>, can be used
|
||||
unmodified.</p>
|
||||
|
||||
<p>Building from the source distribution on a Windows system involves the following steps.
|
||||
Below, the build directory is referred to as <code>BUILDDIR</code>, and the distribution
|
||||
is in <code>\dist\lcc</code>.
|
||||
|
||||
<ol>
|
||||
<li>Create the build directory, perhaps using a version- and platform-specific naming
|
||||
convention as suggested in <a HREF="#unix"><em>Installation on UNIX</em></a>, and record
|
||||
the name of this directory in the <code>BUILDDIR</code> environment variable:<blockquote>
|
||||
<pre>C:\dist\lcc>set BUILDDIR=\progra~1\lcc\<i>version</i>\bin
|
||||
C:\dist\lcc>mkdir %BUILDDIR%</pre>
|
||||
</blockquote>
|
||||
<p>The default build, or installation, directory is <code>\Program Files\lcc\</code><i>version</i><code>\bin</code>,
|
||||
where <i>version</i> is the version number, e.g., 4.2, but the <code>nmake</code> commands require that you use the corresponding 8.3 file name, <code>progra~1</code>,
|
||||
instead of <code>Program Files</code>.</p>
|
||||
</li>
|
||||
<li><a HREF="../etc/win32.c"><code>etc\win32.c</code></a> is the Windows-specific part of
|
||||
the driver. It assumes that environment variable <code>include</code> gives the locations
|
||||
of the VC header files and that the linker (<code>link.exe</code>) and the assembler (<code>ml.exe</code>)
|
||||
are on the PATH. It also assumes that the macro <code>LCCDIR</code> gives the build
|
||||
directory. If necessary, revise a copy of <a HREF="../etc/win32.c"><code>etc\win32.c</code></a>
|
||||
to reflect the conventions on your computer (see <a HREF="#driver"><em>Building the Driver</em></a>),
|
||||
then build the driver, specifying the default temporary directory, if necessary:<blockquote>
|
||||
<pre>C:\dist\lcc>nmake -f makefile.nt HOSTFILE=etc/win32.c lcc
|
||||
...
|
||||
cl -nologo -Zi -MLd -Fd%BUILDDIR%\ -c -Fo%BUILDDIR%\lcc.obj etc/lcc.c
|
||||
lcc.c
|
||||
cl -nologo -Zi -MLd -Fd%BUILDDIR%\ -c -Fo%BUILDDIR%\host.obj etc/win32.c
|
||||
win32.c
|
||||
cl -nologo -Zi -MLd -Fd%BUILDDIR%\ -Fe%BUILDDIR%\lcc.exe %BUILDDIR%\lcc.obj %BUILDDIR%\host.obj</pre>
|
||||
</blockquote>
|
||||
<p>If you make a copy of <code>etc\win32.c</code>, specify the path of the copy as the
|
||||
value of <code>HOSTFILE</code>. For example, if you copy <code>etc\win32.c</code> to <code>BUILDDIR</code>
|
||||
and edit it, use the command</p>
|
||||
<blockquote>
|
||||
<pre>C:\dist\lcc>nmake -f makefile.nt HOSTFILE=%BUILDDIR%\win32.c lcc</pre>
|
||||
</blockquote>
|
||||
</li>
|
||||
<li>Build the preprocessor, compiler proper, library, and other accessories (see <a
|
||||
HREF="#rcc"><em>Building the Compiler</em></a>):<blockquote>
|
||||
<pre>C:\dist\lcc>nmake -f makefile.nt all</pre>
|
||||
</blockquote>
|
||||
<p>This command uses the VC command-line tools <code>cl</code> and <code>lib</code> to
|
||||
build <code>bprint.exe</code>, <code>cpp.exe</code>, <code>lburg.exe</code>, <code>liblcc.lib</code>,
|
||||
<code>librcc.lib</code>, and <code>rcc.exe</code>, all in <code>BUILDDIR</code>. There may
|
||||
be some warnings, but there should be no warnings.</p>
|
||||
</li>
|
||||
<li>Create a test directory and run the test suite:<blockquote>
|
||||
<pre>C:\dist\lcc>mkdir %BUILDDIR%\x86\win32\tst
|
||||
C:\dist\lcc>nmake -f makefile.nt test</pre>
|
||||
</blockquote>
|
||||
<p>This command compiles each program in <a HREF="../tst">tst</a>, compares the generated
|
||||
assembly code and diagnostics with the expected assembly code and diagnostics, executes
|
||||
the program, and compares the output with the expected output (using <code>fc</code>). For
|
||||
example, when the nmake command compiles <a HREF="../tst/8q.c"><code>tst\8q.c</code></a>,
|
||||
it leaves the generated assembly code and diagnostic output in <code>%BUILDDIR%\x86\win32\tst\8q.s</code>
|
||||
and <code>%BUILDDIR%\x86\win32\tst\8q.2</code>, and it compares them with the expected
|
||||
results in <code>x86\win32\tst\8q.sbk</code>. It builds the executable program in <code>%BUILDDIR%\x86\win32\tst\8q.exe</code>,
|
||||
runs it, and redirects the output to <code>%BUILDDIR%\x86\win32\tst\8q.1</code>, which it
|
||||
compares with <code>x86\win32\tst\8q.1bk</code>. The output from this step is voluminous,
|
||||
but there should be no differences and no errors.</p>
|
||||
</li>
|
||||
<li>Run the "triple" test, which compiles <code>rcc</code> with itself and
|
||||
verifies the results:<blockquote>
|
||||
<pre>C:\dist\lcc>nmake -f makefile.nt triple
|
||||
...
|
||||
Assembling: C:/TEMP/lcc2001.asm
|
||||
fc /b %BUILDDIR%\1rcc.exe %BUILDDIR%\2rcc.exe
|
||||
Comparing files %BUILDDIR%\1rcc.exe and %BUILDDIR%\2RCC.EXE
|
||||
00000088: B4 D5</pre>
|
||||
</blockquote>
|
||||
<p>This command builds <code>rcc</code> twice; once using the <code>rcc</code> built by VC
|
||||
and again using the <code>rcc</code> built by <code>lcc</code>. The resulting binaries are
|
||||
compared using <code>fc</code>. They should be identical, except for one or two bytes of
|
||||
timestamp data, as shown at the end of the output above (which will be
|
||||
different on your system). If <code>1rcc.exe</code> and <code>2rcc.exe</code> aren't
|
||||
identical, our compiler is
|
||||
generating incorrect code; <a HREF="#bugs">contact</a> us.</p>
|
||||
</li>
|
||||
<li>Copy <code>lcc.exe</code> and <code>bprint.exe</code> to a directory on your PATH, e.g.,<blockquote>
|
||||
<pre>C:\dist\lcc>copy %BUILDDIR%\lcc.exe \bin
|
||||
1 file(s) copied.
|
||||
|
||||
C:\dist\lcc>copy %BUILDDIR%\bprint.exe \bin
|
||||
1 file(s) copied.</pre>
|
||||
</blockquote>
|
||||
</li>
|
||||
<li>Finally, clean up:<blockquote>
|
||||
<pre>C:\dist\lcc>nmake -f makefile.nt clean</pre>
|
||||
</blockquote>
|
||||
<p>This command removes the derived files in <code>BUILDDIR</code>, but does not remove <code>rcc.exe</code>,
|
||||
etc.; "<code>nmake -f makefile.nt clobber</code>" cleans up and removes all
|
||||
executables and libraries. Test directories under <code>BUILDDIR</code> are <em>not</em>
|
||||
removed; you'll need to remove these by hand, e.g.,</p>
|
||||
<blockquote>
|
||||
<pre>C:\dist\lcc>rmdir %BUILDDIR%\x86 /s
|
||||
%BUILDDIR%\x86, Are you sure (Y/N)? y</pre>
|
||||
</blockquote>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<h2><a NAME="bugs">Reporting Bugs</a></h2>
|
||||
|
||||
<p>lcc is a large, complex program. We find and repair errors routinely. If you think that
|
||||
you've found a error, follow the steps below, which are adapted from the instructions in
|
||||
Chapter 1 of <cite>A Retargetable C Compiler: Design and Implementation</cite>.
|
||||
|
||||
<ol>
|
||||
<li>If you don't have a source file that displays the error, create one. Most errors are
|
||||
exposed when programmers try to compile a program they think is valid, so you probably
|
||||
have a demonstration program already.</li>
|
||||
<li>Preprocess the source file and capture the preprocessor output. Discard the original
|
||||
code.</li>
|
||||
<li>Prune your source code until it can be pruned no more without sending the error into
|
||||
hiding. We prune most error demonstrations to fewer than five lines.</li>
|
||||
<li>Confirm that the source file displays the error with the <em>distributed</em> version of
|
||||
lcc. If you've changed lcc and the error appears only in your version, then you'll have to
|
||||
chase the error yourself, even if it turns out to be our fault, because we can't work on
|
||||
your code.</li>
|
||||
<li>Annotate your code with comments that explain why you think that lcc is
|
||||
wrong. If lcc dies with an assertion failure, please tell us where it died. If
|
||||
lcc crashes, please report the last part of the call chain if you can. If lcc
|
||||
is rejecting a program you think is valid, please tell us why you think it's
|
||||
valid, and include supporting page numbers in the ANSI Standard or the
|
||||
appropriate section in <cite>C: A Reference Manual</cite>, 4th edition by S. B. Harbison
|
||||
and G. L. Steele, Jr. (Prentice Hall, 1995). If lcc silently generates incorrect code for
|
||||
some construct, please include the corrupt assembly code in the comments and flag the
|
||||
incorrect instructions if you can.</li>
|
||||
<li>Confirm that your error hasn't been fixed already. The latest version of lcc is always
|
||||
available for anonymous <code>ftp</code> from <code>ftp.cs.princeton.edu</code> in <a
|
||||
HREF="ftp://ftp.cs.princeton.edu/pub/lcc"><code>pub/lcc</code></a>. A <a
|
||||
HREF="ftp://ftp.cs.princeton.edu/pub/lcc/README"><code>README</code></a> file there gives
|
||||
acquisition details, and the <a HREF="../LOG"><code>LOG</code></a> file reports what errors
|
||||
were fixed and when they were fixed. If you report a error that's been fixed, you might
|
||||
get a canned reply.</li>
|
||||
<li>Post your program to the newsgroup <a href="news:comp.compilers.lcc"><code>comp.compilers.lcc</code></a>
|
||||
using a USENET newsreader like those at <a href="http://www.dejanews.com/">http://www.dejanews.com/</a>
|
||||
and <a href="http://groups.google.com/">http://groups.google.com/</a>.
|
||||
Please post only valid C programs; put all remarks in C comments so that we can process
|
||||
reports semi automatically.</li>
|
||||
</ol>
|
||||
|
||||
<h2><a NAME="mailinglist">Keeping in Touch</a></h2>
|
||||
|
||||
<p>The USENET newsgroup <a href="news:comp.compilers.lcc">comp.compilers.lcc</a> is an
|
||||
unmoderated newsgroup that serves as a forum for all topics related to the installation,
|
||||
use, and development of lcc. You can post messages to comp.compilers.lcc using any USENET
|
||||
newsreader or by visiting <a href="http://www.dejanews.com/">http://www.dejanews.com/</a>,
|
||||
which also includes an archive of recent postings.</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<address>
|
||||
<a HREF="http://www.research.microsoft.com/~cwfraser/">Chris Fraser</a> / <a
|
||||
HREF="mailto:cwfraser@microsoft.com">cwfraser@microsoft.com</a><br>
|
||||
<a HREF="http://www.research.microsoft.com/~drh/">David Hanson</a> / <a
|
||||
HREF="mailto:drh@microsoft.com">drh@microsoft.com</a><br>
|
||||
$Revision: 1.45 $ $Date: 2002/09/04 18:33:24 $
|
||||
</address>
|
||||
</body>
|
||||
</html>
|
||||
550
src/cmd/lccom-1/enode.c
Normal file
550
src/cmd/lccom-1/enode.c
Normal file
@@ -0,0 +1,550 @@
|
||||
#include "c.h"
|
||||
|
||||
static Tree addtree(int, Tree, Tree);
|
||||
static Tree andtree(int, Tree, Tree);
|
||||
static Tree cmptree(int, Tree, Tree);
|
||||
static int compatible(Type, Type);
|
||||
static int isnullptr(Tree e);
|
||||
static Tree multree(int, Tree, Tree);
|
||||
static Tree subtree(int, Tree, Tree);
|
||||
#define isvoidptr(ty) \
|
||||
(isptr(ty) && unqual(ty->type) == voidtype)
|
||||
|
||||
Tree (*optree[])(int, Tree, Tree) = {
|
||||
#define xx(a,b,c,d,e,f,g) e,
|
||||
#define yy(a,b,c,d,e,f,g) e,
|
||||
#include "token.h"
|
||||
};
|
||||
Tree call(Tree f, Type fty, Coordinate src) {
|
||||
int n = 0;
|
||||
Tree args = NULL, r = NULL, e;
|
||||
Type *proto, rty = unqual(freturn(fty));
|
||||
Symbol t3 = NULL;
|
||||
|
||||
if (fty->u.f.oldstyle)
|
||||
proto = NULL;
|
||||
else
|
||||
proto = fty->u.f.proto;
|
||||
if (hascall(f))
|
||||
r = f;
|
||||
if (isstruct(rty))
|
||||
{
|
||||
t3 = temporary(AUTO, unqual(rty));
|
||||
if (rty->size == 0)
|
||||
error("illegal use of incomplete type `%t'\n", rty);
|
||||
}
|
||||
if (t != ')')
|
||||
for (;;) {
|
||||
Tree q = pointer(expr1(0));
|
||||
if (proto && *proto && *proto != voidtype)
|
||||
{
|
||||
Type aty;
|
||||
q = value(q);
|
||||
aty = assign(*proto, q);
|
||||
if (aty)
|
||||
q = cast(q, aty);
|
||||
else
|
||||
error("type error in argument %d to %s; found `%t' expected `%t'\n", n + 1, funcname(f),
|
||||
|
||||
q->type, *proto);
|
||||
if ((isint(q->type) || isenum(q->type))
|
||||
&& q->type->size != inttype->size)
|
||||
q = cast(q, promote(q->type));
|
||||
++proto;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!fty->u.f.oldstyle && *proto == NULL)
|
||||
error("too many arguments to %s\n", funcname(f));
|
||||
q = value(q);
|
||||
if (isarray(q->type) || q->type->size == 0)
|
||||
error("type error in argument %d to %s; `%t' is illegal\n", n + 1, funcname(f), q->type);
|
||||
|
||||
else
|
||||
q = cast(q, promote(q->type));
|
||||
}
|
||||
if (!IR->wants_argb && isstruct(q->type)) {
|
||||
if (iscallb(q))
|
||||
q = addrof(q);
|
||||
else {
|
||||
Symbol t1 = temporary(AUTO, unqual(q->type));
|
||||
q = asgn(t1, q);
|
||||
q = tree(RIGHT, ptr(t1->type),
|
||||
root(q), lvalue(idtree(t1)));
|
||||
}
|
||||
}
|
||||
if (q->type->size == 0)
|
||||
q->type = inttype;
|
||||
if (hascall(q))
|
||||
r = r ? tree(RIGHT, voidtype, r, q) : q;
|
||||
args = tree(mkop(ARG, q->type), q->type, q, args);
|
||||
n++;
|
||||
if (Aflag >= 2 && n == 32)
|
||||
warning("more than 31 arguments in a call to %s\n",
|
||||
funcname(f));
|
||||
if (t != ',')
|
||||
break;
|
||||
t = gettok();
|
||||
}
|
||||
expect(')');
|
||||
if (proto && *proto && *proto != voidtype)
|
||||
error("insufficient number of arguments to %s\n",
|
||||
funcname(f));
|
||||
if (r)
|
||||
args = tree(RIGHT, voidtype, r, args);
|
||||
e = calltree(f, rty, args, t3);
|
||||
if (events.calls)
|
||||
apply(events.calls, &src, &e);
|
||||
return e;
|
||||
}
|
||||
Tree calltree(Tree f, Type ty, Tree args, Symbol t3) {
|
||||
Tree p;
|
||||
|
||||
if (args)
|
||||
f = tree(RIGHT, f->type, args, f);
|
||||
if (isstruct(ty)) {
|
||||
assert(t3);
|
||||
p = tree(RIGHT, ty,
|
||||
tree(CALL+B, ty, f, addrof(idtree(t3))),
|
||||
idtree(t3));
|
||||
} else {
|
||||
Type rty = ty;
|
||||
if (isenum(ty))
|
||||
rty = unqual(ty)->type;
|
||||
if (!isfloat(rty))
|
||||
rty = promote(rty);
|
||||
p = tree(mkop(CALL, rty), rty, f, NULL);
|
||||
if (isptr(ty) || p->type->size > ty->size)
|
||||
p = cast(p, ty);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
Tree vcall(Symbol func, Type ty, ...) {
|
||||
va_list ap;
|
||||
Tree args = NULL, e, f = pointer(idtree(func)), r = NULL;
|
||||
|
||||
assert(isfunc(func->type));
|
||||
if (ty == NULL)
|
||||
ty = freturn(func->type);
|
||||
va_start(ap, ty);
|
||||
while ((e = va_arg(ap, Tree)) != NULL) {
|
||||
if (hascall(e))
|
||||
r = r == NULL ? e : tree(RIGHT, voidtype, r, e);
|
||||
args = tree(mkop(ARG, e->type), e->type, e, args);
|
||||
}
|
||||
va_end(ap);
|
||||
if (r != NULL)
|
||||
args = tree(RIGHT, voidtype, r, args);
|
||||
return calltree(f, ty, args, NULL);
|
||||
}
|
||||
int iscallb(Tree e) {
|
||||
return e->op == RIGHT && e->kids[0] && e->kids[1]
|
||||
&& e->kids[0]->op == CALL+B
|
||||
&& e->kids[1]->op == INDIR+B
|
||||
&& isaddrop(e->kids[1]->kids[0]->op)
|
||||
&& e->kids[1]->kids[0]->u.sym->temporary;
|
||||
}
|
||||
|
||||
static Tree addtree(int op, Tree l, Tree r) {
|
||||
Type ty = inttype;
|
||||
|
||||
if (isarith(l->type) && isarith(r->type)) {
|
||||
ty = binary(l->type, r->type);
|
||||
l = cast(l, ty);
|
||||
r = cast(r, ty);
|
||||
} else if (isptr(l->type) && isint(r->type))
|
||||
return addtree(ADD, r, l);
|
||||
else if ( isptr(r->type) && isint(l->type)
|
||||
&& !isfunc(r->type->type))
|
||||
{
|
||||
long n;
|
||||
ty = unqual(r->type);
|
||||
n = unqual(ty->type)->size;
|
||||
if (n == 0)
|
||||
error("unknown size for type `%t'\n", ty->type);
|
||||
l = cast(l, promote(l->type));
|
||||
if (n > 1)
|
||||
l = multree(MUL, cnsttree(signedptr, n), l);
|
||||
if (isunsigned(l->type))
|
||||
l = cast(l, unsignedptr);
|
||||
else
|
||||
l = cast(l, signedptr);
|
||||
if (YYcheck && !isaddrop(r->op)) /* omit */
|
||||
return nullcall(ty, YYcheck, r, l); /* omit */
|
||||
return simplify(ADD, ty, l, r);
|
||||
}
|
||||
|
||||
else
|
||||
typeerror(op, l, r);
|
||||
return simplify(op, ty, l, r);
|
||||
}
|
||||
|
||||
Tree cnsttree(Type ty, ...) {
|
||||
Tree p = tree(mkop(CNST,ty), ty, NULL, NULL);
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, ty);
|
||||
switch (ty->op) {
|
||||
case INT: p->u.v.i = va_arg(ap, long); break;
|
||||
case UNSIGNED:p->u.v.u = va_arg(ap, unsigned long)&ones(8*ty->size); break;
|
||||
case FLOAT: p->u.v.d = va_arg(ap, long double); break;
|
||||
case POINTER: p->u.v.p = va_arg(ap, void *); break;
|
||||
default: assert(0);
|
||||
}
|
||||
va_end(ap);
|
||||
return p;
|
||||
}
|
||||
|
||||
Tree consttree(unsigned n, Type ty) {
|
||||
if (isarray(ty))
|
||||
ty = atop(ty);
|
||||
else assert(isint(ty));
|
||||
return cnsttree(ty, (unsigned long)n);
|
||||
}
|
||||
static Tree cmptree(int op, Tree l, Tree r) {
|
||||
Type ty;
|
||||
|
||||
if (isarith(l->type) && isarith(r->type)) {
|
||||
ty = binary(l->type, r->type);
|
||||
l = cast(l, ty);
|
||||
r = cast(r, ty);
|
||||
} else if (compatible(l->type, r->type)) {
|
||||
ty = unsignedptr;
|
||||
l = cast(l, ty);
|
||||
r = cast(r, ty);
|
||||
} else {
|
||||
ty = unsignedtype;
|
||||
typeerror(op, l, r);
|
||||
}
|
||||
return simplify(mkop(op,ty), inttype, l, r);
|
||||
}
|
||||
static int compatible(Type ty1, Type ty2) {
|
||||
ty1 = unqual(ty1);
|
||||
ty2 = unqual(ty2);
|
||||
return isptr(ty1) && !isfunc(ty1->type)
|
||||
&& isptr(ty2) && !isfunc(ty2->type)
|
||||
&& eqtype(unqual(ty1->type), unqual(ty2->type), 0);
|
||||
}
|
||||
static int isnullptr(Tree e) {
|
||||
Type ty = unqual(e->type);
|
||||
|
||||
return generic(e->op) == CNST
|
||||
&& ((ty->op == INT && e->u.v.i == 0)
|
||||
|| (ty->op == UNSIGNED && e->u.v.u == 0)
|
||||
|| (isvoidptr(ty) && e->u.v.p == NULL));
|
||||
}
|
||||
Tree eqtree(int op, Tree l, Tree r) {
|
||||
Type xty = unqual(l->type), yty = unqual(r->type);
|
||||
|
||||
if ((isptr(xty) && isnullptr(r))
|
||||
|| (isptr(xty) && !isfunc(xty->type) && isvoidptr(yty))
|
||||
|| (isptr(xty) && isptr(yty)
|
||||
&& eqtype(unqual(xty->type), unqual(yty->type), 1))) {
|
||||
Type ty = unsignedptr;
|
||||
l = cast(l, ty);
|
||||
r = cast(r, ty);
|
||||
return simplify(mkop(op,ty), inttype, l, r);
|
||||
}
|
||||
if ((isptr(yty) && isnullptr(l))
|
||||
|| (isptr(yty) && !isfunc(yty->type) && isvoidptr(xty)))
|
||||
return eqtree(op, r, l);
|
||||
return cmptree(op, l, r);
|
||||
}
|
||||
|
||||
Type assign(Type xty, Tree e) {
|
||||
Type yty = unqual(e->type);
|
||||
|
||||
xty = unqual(xty);
|
||||
if (isenum(xty))
|
||||
xty = xty->type;
|
||||
if (xty->size == 0 || yty->size == 0)
|
||||
return NULL;
|
||||
if ((isarith(xty) && isarith(yty))
|
||||
|| (isstruct(xty) && xty == yty))
|
||||
return xty;
|
||||
if (isptr(xty) && isnullptr(e))
|
||||
return xty;
|
||||
if (((isvoidptr(xty) && isptr(yty))
|
||||
|| (isptr(xty) && isvoidptr(yty)))
|
||||
&& ( (isconst(xty->type) || !isconst(yty->type))
|
||||
&& (isvolatile(xty->type) || !isvolatile(yty->type))))
|
||||
return xty;
|
||||
|
||||
if ((isptr(xty) && isptr(yty)
|
||||
&& eqtype(unqual(xty->type), unqual(yty->type), 1))
|
||||
&& ( (isconst(xty->type) || !isconst(yty->type))
|
||||
&& (isvolatile(xty->type) || !isvolatile(yty->type))))
|
||||
return xty;
|
||||
if (isptr(xty) && isptr(yty)
|
||||
&& ( (isconst(xty->type) || !isconst(yty->type))
|
||||
&& (isvolatile(xty->type) || !isvolatile(yty->type)))) {
|
||||
Type lty = unqual(xty->type), rty = unqual(yty->type);
|
||||
if ((isenum(lty) && rty == inttype)
|
||||
|| (isenum(rty) && lty == inttype)) {
|
||||
if (Aflag >= 1)
|
||||
warning("assignment between `%t' and `%t' is compiler-dependent\n",
|
||||
xty, yty);
|
||||
return xty;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
Tree asgntree(int op, Tree l, Tree r) {
|
||||
Type aty, ty;
|
||||
|
||||
r = pointer(r);
|
||||
ty = assign(l->type, r);
|
||||
if (ty)
|
||||
r = cast(r, ty);
|
||||
else {
|
||||
typeerror(ASGN, l, r);
|
||||
if (r->type == voidtype)
|
||||
r = retype(r, inttype);
|
||||
ty = r->type;
|
||||
}
|
||||
if (l->op != FIELD)
|
||||
l = lvalue(l);
|
||||
aty = l->type;
|
||||
if (isptr(aty))
|
||||
aty = unqual(aty)->type;
|
||||
if ( isconst(aty)
|
||||
|| (isstruct(aty) && unqual(aty)->u.sym->u.s.cfields)) {
|
||||
if (isaddrop(l->op)
|
||||
&& !l->u.sym->computed && !l->u.sym->generated)
|
||||
error("assignment to const identifier `%s'\n",
|
||||
l->u.sym->name);
|
||||
else
|
||||
error("assignment to const location\n");
|
||||
}
|
||||
if (l->op == FIELD) {
|
||||
long n = 8*l->u.field->type->size - fieldsize(l->u.field);
|
||||
if (n > 0 && isunsigned(l->u.field->type))
|
||||
r = bittree(BAND, r,
|
||||
cnsttree(r->type, (unsigned long)fieldmask(l->u.field)));
|
||||
else if (n > 0) {
|
||||
if (r->op == CNST+I) {
|
||||
n = r->u.v.i;
|
||||
if (n&(1<<(fieldsize(l->u.field)-1)))
|
||||
n |= ~0UL<<fieldsize(l->u.field);
|
||||
r = cnsttree(r->type, n);
|
||||
} else
|
||||
r = shtree(RSH,
|
||||
shtree(LSH, r, cnsttree(inttype, n)),
|
||||
cnsttree(inttype, n));
|
||||
}
|
||||
}
|
||||
if (isstruct(ty) && isaddrop(l->op) && iscallb(r))
|
||||
return tree(RIGHT, ty,
|
||||
tree(CALL+B, ty, r->kids[0]->kids[0], l),
|
||||
idtree(l->u.sym));
|
||||
return tree(mkop(op,ty), ty, l, r);
|
||||
}
|
||||
Tree condtree(Tree e, Tree l, Tree r) {
|
||||
Symbol t1;
|
||||
Type ty, xty = l->type, yty = r->type;
|
||||
Tree p;
|
||||
|
||||
if (isarith(xty) && isarith(yty))
|
||||
ty = binary(xty, yty);
|
||||
else if (eqtype(xty, yty, 1))
|
||||
ty = unqual(xty);
|
||||
else if (isptr(xty) && isnullptr(r))
|
||||
ty = xty;
|
||||
else if (isnullptr(l) && isptr(yty))
|
||||
ty = yty;
|
||||
else if ((isptr(xty) && !isfunc(xty->type) && isvoidptr(yty))
|
||||
|| (isptr(yty) && !isfunc(yty->type) && isvoidptr(xty)))
|
||||
ty = voidptype;
|
||||
else if ((isptr(xty) && isptr(yty)
|
||||
&& eqtype(unqual(xty->type), unqual(yty->type), 1)))
|
||||
ty = xty;
|
||||
else {
|
||||
typeerror(COND, l, r);
|
||||
return consttree(0, inttype);
|
||||
}
|
||||
if (isptr(ty)) {
|
||||
ty = unqual(unqual(ty)->type);
|
||||
if ((isptr(xty) && isconst(unqual(xty)->type))
|
||||
|| (isptr(yty) && isconst(unqual(yty)->type)))
|
||||
ty = qual(CONST, ty);
|
||||
if ((isptr(xty) && isvolatile(unqual(xty)->type))
|
||||
|| (isptr(yty) && isvolatile(unqual(yty)->type)))
|
||||
ty = qual(VOLATILE, ty);
|
||||
ty = ptr(ty);
|
||||
}
|
||||
switch (e->op) {
|
||||
case CNST+I: return cast(e->u.v.i != 0 ? l : r, ty);
|
||||
case CNST+U: return cast(e->u.v.u != 0 ? l : r, ty);
|
||||
case CNST+P: return cast(e->u.v.p != 0 ? l : r, ty);
|
||||
case CNST+F: return cast(e->u.v.d != 0.0 ? l : r, ty);
|
||||
}
|
||||
if (ty != voidtype && ty->size > 0) {
|
||||
t1 = genident(REGISTER, unqual(ty), level);
|
||||
/* t1 = temporary(REGISTER, unqual(ty)); */
|
||||
l = asgn(t1, l);
|
||||
r = asgn(t1, r);
|
||||
} else
|
||||
t1 = NULL;
|
||||
p = tree(COND, ty, cond(e),
|
||||
tree(RIGHT, ty, root(l), root(r)));
|
||||
p->u.sym = t1;
|
||||
return p;
|
||||
}
|
||||
/* addrof - address of p */
|
||||
Tree addrof(Tree p) {
|
||||
Tree q = p;
|
||||
|
||||
for (;;)
|
||||
switch (generic(q->op)) {
|
||||
case RIGHT:
|
||||
assert(q->kids[0] || q->kids[1]);
|
||||
q = q->kids[1] ? q->kids[1] : q->kids[0];
|
||||
continue;
|
||||
case ASGN:
|
||||
q = q->kids[1];
|
||||
continue;
|
||||
case COND: {
|
||||
Symbol t1 = q->u.sym;
|
||||
q->u.sym = 0;
|
||||
q = idtree(t1);
|
||||
/* fall thru */
|
||||
}
|
||||
case INDIR:
|
||||
if (p == q)
|
||||
return q->kids[0];
|
||||
q = q->kids[0];
|
||||
return tree(RIGHT, q->type, root(p), q);
|
||||
default:
|
||||
error("addressable object required\n");
|
||||
return value(p);
|
||||
}
|
||||
}
|
||||
|
||||
/* andtree - construct tree for l [&& ||] r */
|
||||
static Tree andtree(int op, Tree l, Tree r) {
|
||||
if (!isscalar(l->type) || !isscalar(r->type))
|
||||
typeerror(op, l, r);
|
||||
return simplify(op, inttype, cond(l), cond(r));
|
||||
}
|
||||
|
||||
/* asgn - generate tree for assignment of expr e to symbol p sans qualifiers */
|
||||
Tree asgn(Symbol p, Tree e) {
|
||||
if (isarray(p->type))
|
||||
e = tree(ASGN+B, p->type, idtree(p),
|
||||
tree(INDIR+B, e->type, e, NULL));
|
||||
else {
|
||||
Type ty = p->type;
|
||||
p->type = unqual(p->type);
|
||||
if (isstruct(p->type) && p->type->u.sym->u.s.cfields) {
|
||||
p->type->u.sym->u.s.cfields = 0;
|
||||
e = asgntree(ASGN, idtree(p), e);
|
||||
p->type->u.sym->u.s.cfields = 1;
|
||||
} else
|
||||
e = asgntree(ASGN, idtree(p), e);
|
||||
p->type = ty;
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
/* bittree - construct tree for l [& | ^ %] r */
|
||||
Tree bittree(int op, Tree l, Tree r) {
|
||||
Type ty = inttype;
|
||||
|
||||
if (isint(l->type) && isint(r->type)) {
|
||||
ty = binary(l->type, r->type);
|
||||
l = cast(l, ty);
|
||||
r = cast(r, ty);
|
||||
} else
|
||||
typeerror(op, l, r);
|
||||
return simplify(op, ty, l, r);
|
||||
}
|
||||
|
||||
/* multree - construct tree for l [* /] r */
|
||||
static Tree multree(int op, Tree l, Tree r) {
|
||||
Type ty = inttype;
|
||||
|
||||
if (isarith(l->type) && isarith(r->type)) {
|
||||
ty = binary(l->type, r->type);
|
||||
l = cast(l, ty);
|
||||
r = cast(r, ty);
|
||||
} else
|
||||
typeerror(op, l, r);
|
||||
return simplify(op, ty, l, r);
|
||||
}
|
||||
|
||||
/* shtree - construct tree for l [>> <<] r */
|
||||
Tree shtree(int op, Tree l, Tree r) {
|
||||
Type ty = inttype;
|
||||
|
||||
if (isint(l->type) && isint(r->type)) {
|
||||
ty = promote(l->type);
|
||||
l = cast(l, ty);
|
||||
r = cast(r, inttype);
|
||||
} else
|
||||
typeerror(op, l, r);
|
||||
return simplify(op, ty, l, r);
|
||||
}
|
||||
|
||||
/* subtree - construct tree for l - r */
|
||||
static Tree subtree(int op, Tree l, Tree r) {
|
||||
long n;
|
||||
Type ty = inttype;
|
||||
|
||||
if (isarith(l->type) && isarith(r->type)) {
|
||||
ty = binary(l->type, r->type);
|
||||
l = cast(l, ty);
|
||||
r = cast(r, ty);
|
||||
} else if (isptr(l->type) && !isfunc(l->type->type) && isint(r->type)) {
|
||||
ty = unqual(l->type);
|
||||
n = unqual(ty->type)->size;
|
||||
if (n == 0)
|
||||
error("unknown size for type `%t'\n", ty->type);
|
||||
r = cast(r, promote(r->type));
|
||||
if (n > 1)
|
||||
r = multree(MUL, cnsttree(signedptr, n), r);
|
||||
if (isunsigned(r->type))
|
||||
r = cast(r, unsignedptr);
|
||||
else
|
||||
r = cast(r, signedptr);
|
||||
return simplify(SUB+P, ty, l, r);
|
||||
} else if (compatible(l->type, r->type)) {
|
||||
ty = unqual(l->type);
|
||||
n = unqual(ty->type)->size;
|
||||
if (n == 0)
|
||||
error("unknown size for type `%t'\n", ty->type);
|
||||
l = simplify(SUB+U, unsignedptr,
|
||||
cast(l, unsignedptr), cast(r, unsignedptr));
|
||||
return simplify(DIV+I, longtype,
|
||||
cast(l, longtype), cnsttree(longtype, n));
|
||||
} else
|
||||
typeerror(op, l, r);
|
||||
return simplify(op, ty, l, r);
|
||||
}
|
||||
|
||||
/* typeerror - issue "operands of op have illegal types `l' and `r'" */
|
||||
void typeerror(int op, Tree l, Tree r) {
|
||||
int i;
|
||||
static struct { int op; char *name; } ops[] = {
|
||||
{ ASGN, "=" }, { INDIR, "*" }, { NEG, "-" },
|
||||
{ ADD, "+" }, { SUB, "-" }, { LSH, "<<"},
|
||||
{ MOD, "%" }, { RSH, ">>"}, { BAND, "&" },
|
||||
{ BCOM, "~" }, { BOR, "|" }, { BXOR, "^" },
|
||||
{ DIV, "/" }, { MUL, "*" }, { EQ, "=="},
|
||||
{ GE, ">="}, { GT, ">" }, { LE, "<="},
|
||||
{ LT, "<" }, { NE, "!="}, { AND, "&&"},
|
||||
{ NOT, "!" }, { OR, "||"}, { COND, "?:"},
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
op = generic(op);
|
||||
for (i = 0; ops[i].op; i++)
|
||||
if (op == ops[i].op)
|
||||
break;
|
||||
assert(ops[i].name);
|
||||
if (r)
|
||||
error("operands of %s have illegal types `%t' and `%t'\n",
|
||||
ops[i].name, l->type, r->type);
|
||||
else
|
||||
error("operand of unary %s has illegal type `%t'\n", ops[i].name,
|
||||
l->type);
|
||||
}
|
||||
136
src/cmd/lccom-1/error.c
Normal file
136
src/cmd/lccom-1/error.c
Normal file
@@ -0,0 +1,136 @@
|
||||
#include "c.h"
|
||||
|
||||
static void printtoken(void);
|
||||
int errcnt = 0;
|
||||
int errlimit = 20;
|
||||
char kind[] = {
|
||||
#define xx(a,b,c,d,e,f,g) f,
|
||||
#define yy(a,b,c,d,e,f,g) f,
|
||||
#include "token.h"
|
||||
};
|
||||
int wflag; /* != 0 to suppress warning messages */
|
||||
|
||||
void test(int tok, char set[]) {
|
||||
if (t == tok)
|
||||
t = gettok();
|
||||
else {
|
||||
expect(tok);
|
||||
skipto(tok, set);
|
||||
if (t == tok)
|
||||
t = gettok();
|
||||
}
|
||||
}
|
||||
void expect(int tok) {
|
||||
if (t == tok)
|
||||
t = gettok();
|
||||
else {
|
||||
error("syntax error; found");
|
||||
printtoken();
|
||||
fprint(stderr, " expecting `%k'\n", tok);
|
||||
}
|
||||
}
|
||||
void error(const char *fmt, ...) {
|
||||
va_list ap;
|
||||
|
||||
if (errcnt++ >= errlimit) {
|
||||
errcnt = -1;
|
||||
error("too many errors\n");
|
||||
exit(1);
|
||||
}
|
||||
va_start(ap, fmt);
|
||||
if (firstfile != file && firstfile && *firstfile)
|
||||
fprint(stderr, "%s: ", firstfile);
|
||||
fprint(stderr, "%w: ", &src);
|
||||
vfprint(stderr, NULL, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void skipto(int tok, char set[]) {
|
||||
int n;
|
||||
char *s;
|
||||
|
||||
assert(set);
|
||||
for (n = 0; t != EOI && t != tok; t = gettok()) {
|
||||
for (s = set; *s && kind[t] != *s; s++)
|
||||
;
|
||||
if (kind[t] == *s)
|
||||
break;
|
||||
if (n++ == 0)
|
||||
error("skipping");
|
||||
if (n <= 8)
|
||||
printtoken();
|
||||
else if (n == 9)
|
||||
fprint(stderr, " ...");
|
||||
}
|
||||
if (n > 8) {
|
||||
fprint(stderr, " up to");
|
||||
printtoken();
|
||||
}
|
||||
if (n > 0)
|
||||
fprint(stderr, "\n");
|
||||
}
|
||||
/* fatal - issue fatal error message and exit */
|
||||
int fatal(const char *name, const char *fmt, int n) {
|
||||
print("\n");
|
||||
errcnt = -1;
|
||||
error("compiler error in %s--", name);
|
||||
fprint(stderr, fmt, n);
|
||||
exit(EXIT_FAILURE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* printtoken - print current token preceeded by a space */
|
||||
static void printtoken(void) {
|
||||
switch (t) {
|
||||
case ID: fprint(stderr, " `%s'", token); break;
|
||||
case ICON:
|
||||
fprint(stderr, " `%s'", vtoa(tsym->type, tsym->u.c.v));
|
||||
break;
|
||||
case SCON: {
|
||||
int i, n;
|
||||
if (ischar(tsym->type->type)) {
|
||||
char *s = tsym->u.c.v.p;
|
||||
n = tsym->type->size;
|
||||
fprint(stderr, " \"");
|
||||
for (i = 0; i < 20 && i < n && *s; s++, i++)
|
||||
if (*s < ' ' || *s >= 0177)
|
||||
fprint(stderr, "\\%o", *s);
|
||||
else
|
||||
fprint(stderr, "%c", *s);
|
||||
} else { /* wchar_t string */
|
||||
unsigned int *s = tsym->u.c.v.p;
|
||||
assert(tsym->type->type->size == widechar->size);
|
||||
n = tsym->type->size/widechar->size;
|
||||
fprint(stderr, " L\"");
|
||||
for (i = 0; i < 20 && i < n && *s; s++, i++)
|
||||
if (*s < ' ' || *s >= 0177)
|
||||
fprint(stderr, "\\x%x", *s);
|
||||
else
|
||||
fprint(stderr, "%c", *s);
|
||||
}
|
||||
if (i < n)
|
||||
fprint(stderr, " ...");
|
||||
else
|
||||
fprint(stderr, "\"");
|
||||
break;
|
||||
}
|
||||
case FCON:
|
||||
fprint(stderr, " `%S'", token, (char*)cp - token);
|
||||
break;
|
||||
case '`': case '\'': fprint(stderr, " \"%k\"", t); break;
|
||||
default: fprint(stderr, " `%k'", t);
|
||||
}
|
||||
}
|
||||
|
||||
/* warning - issue warning error message */
|
||||
void warning(const char *fmt, ...) {
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
if (wflag == 0) {
|
||||
errcnt--;
|
||||
error("warning: ");
|
||||
vfprint(stderr, NULL, fmt, ap);
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
26
src/cmd/lccom-1/event.c
Normal file
26
src/cmd/lccom-1/event.c
Normal file
@@ -0,0 +1,26 @@
|
||||
#include "c.h"
|
||||
|
||||
struct entry {
|
||||
Apply func;
|
||||
void *cl;
|
||||
};
|
||||
|
||||
Events events;
|
||||
void attach(Apply func, void *cl, List *list) {
|
||||
struct entry *p;
|
||||
|
||||
NEW(p, PERM);
|
||||
p->func = func;
|
||||
p->cl = cl;
|
||||
*list = append(p, *list);
|
||||
}
|
||||
void apply(List event, void *arg1, void *arg2) {
|
||||
if (event) {
|
||||
List lp = event;
|
||||
do {
|
||||
struct entry *p = lp->x;
|
||||
(*p->func)(p->cl, arg1, arg2);
|
||||
lp = lp->link;
|
||||
} while (lp != event);
|
||||
}
|
||||
}
|
||||
711
src/cmd/lccom-1/expr.c
Normal file
711
src/cmd/lccom-1/expr.c
Normal file
@@ -0,0 +1,711 @@
|
||||
#include "c.h"
|
||||
|
||||
static char prec[] = {
|
||||
#define xx(a,b,c,d,e,f,g) c,
|
||||
#define yy(a,b,c,d,e,f,g) c,
|
||||
#include "token.h"
|
||||
};
|
||||
static int oper[] = {
|
||||
#define xx(a,b,c,d,e,f,g) d,
|
||||
#define yy(a,b,c,d,e,f,g) d,
|
||||
#include "token.h"
|
||||
};
|
||||
float refinc = 1.0;
|
||||
static Tree expr2(void);
|
||||
static Tree expr3(int);
|
||||
static Tree nullcheck(Tree);
|
||||
static Tree postfix(Tree);
|
||||
static Tree unary(void);
|
||||
static Tree primary(void);
|
||||
static Type super(Type ty);
|
||||
|
||||
static Type super(Type ty) {
|
||||
switch (ty->op) {
|
||||
case INT:
|
||||
if (ty->size < inttype->size)
|
||||
return inttype;
|
||||
break;
|
||||
case UNSIGNED:
|
||||
if (ty->size < unsignedtype->size)
|
||||
return unsignedtype;
|
||||
break;
|
||||
case POINTER:
|
||||
return unsignedptr;
|
||||
}
|
||||
return ty;
|
||||
}
|
||||
Tree expr(int tok) {
|
||||
static char stop[] = { IF, ID, '}', 0 };
|
||||
Tree p = expr1(0);
|
||||
|
||||
while (t == ',') {
|
||||
Tree q;
|
||||
t = gettok();
|
||||
q = pointer(expr1(0));
|
||||
p = tree(RIGHT, q->type, root(value(p)), q);
|
||||
}
|
||||
if (tok)
|
||||
test(tok, stop);
|
||||
return p;
|
||||
}
|
||||
Tree expr0(int tok) {
|
||||
return root(expr(tok));
|
||||
}
|
||||
Tree expr1(int tok) {
|
||||
static char stop[] = { IF, ID, 0 };
|
||||
Tree p = expr2();
|
||||
|
||||
if (t == '='
|
||||
|| (prec[t] >= 6 && prec[t] <= 8)
|
||||
|| (prec[t] >= 11 && prec[t] <= 13)) {
|
||||
int op = t;
|
||||
t = gettok();
|
||||
if (oper[op] == ASGN)
|
||||
p = asgntree(ASGN, p, value(expr1(0)));
|
||||
else
|
||||
{
|
||||
expect('=');
|
||||
p = incr(op, p, expr1(0));
|
||||
}
|
||||
}
|
||||
if (tok)
|
||||
test(tok, stop);
|
||||
return p;
|
||||
}
|
||||
Tree incr(int op, Tree v, Tree e) {
|
||||
return asgntree(ASGN, v, (*optree[op])(oper[op], v, e));
|
||||
}
|
||||
static Tree expr2(void) {
|
||||
Tree p = expr3(4);
|
||||
|
||||
if (t == '?') {
|
||||
Tree l, r;
|
||||
Coordinate pts[2];
|
||||
if (Aflag > 1 && isfunc(p->type))
|
||||
warning("%s used in a conditional expression\n",
|
||||
funcname(p));
|
||||
p = pointer(p);
|
||||
t = gettok();
|
||||
pts[0] = src;
|
||||
l = pointer(expr(':'));
|
||||
pts[1] = src;
|
||||
r = pointer(expr2());
|
||||
if (generic(p->op) != CNST && events.points)
|
||||
{
|
||||
apply(events.points, &pts[0], &l);
|
||||
apply(events.points, &pts[1], &r);
|
||||
}
|
||||
p = condtree(p, l, r);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
Tree value(Tree p) {
|
||||
int op = generic(rightkid(p)->op);
|
||||
|
||||
if (p->type != voidtype
|
||||
&& (op==AND || op==OR || op==NOT || op==EQ || op==NE
|
||||
|| op== LE || op==LT || op== GE || op==GT))
|
||||
p = condtree(p, consttree(1, inttype),
|
||||
consttree(0, inttype));
|
||||
return p;
|
||||
}
|
||||
static Tree expr3(int k) {
|
||||
int k1;
|
||||
Tree p = unary();
|
||||
|
||||
for (k1 = prec[t]; k1 >= k; k1--)
|
||||
while (prec[t] == k1 && *cp != '=') {
|
||||
Tree r;
|
||||
Coordinate pt;
|
||||
int op = t;
|
||||
t = gettok();
|
||||
pt = src;
|
||||
p = pointer(p);
|
||||
if (op == ANDAND || op == OROR) {
|
||||
r = pointer(expr3(k1));
|
||||
if (events.points)
|
||||
apply(events.points, &pt, &r);
|
||||
} else
|
||||
r = pointer(expr3(k1 + 1));
|
||||
p = (*optree[op])(oper[op], p, r);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
static Tree unary(void) {
|
||||
Tree p;
|
||||
|
||||
switch (t) {
|
||||
case '*': t = gettok(); p = unary(); p = pointer(p);
|
||||
if (isptr(p->type)
|
||||
&& (isfunc(p->type->type) || isarray(p->type->type)))
|
||||
p = retype(p, p->type->type);
|
||||
else {
|
||||
if (YYnull)
|
||||
p = nullcheck(p);
|
||||
p = rvalue(p);
|
||||
} break;
|
||||
case '&': t = gettok(); p = unary(); if (isarray(p->type) || isfunc(p->type))
|
||||
p = retype(p, ptr(p->type));
|
||||
else
|
||||
p = lvalue(p);
|
||||
if (isaddrop(p->op) && p->u.sym->sclass == REGISTER)
|
||||
error("invalid operand of unary &; `%s' is declared register\n", p->u.sym->name);
|
||||
|
||||
else if (isaddrop(p->op))
|
||||
p->u.sym->addressed = 1;
|
||||
break;
|
||||
case '+': t = gettok(); p = unary(); p = pointer(p);
|
||||
if (isarith(p->type))
|
||||
p = cast(p, promote(p->type));
|
||||
else
|
||||
typeerror(ADD, p, NULL); break;
|
||||
case '-': t = gettok(); p = unary(); p = pointer(p);
|
||||
if (isarith(p->type)) {
|
||||
Type ty = promote(p->type);
|
||||
p = cast(p, ty);
|
||||
if (isunsigned(ty)) {
|
||||
warning("unsigned operand of unary -\n");
|
||||
p = simplify(ADD, ty, simplify(BCOM, ty, p, NULL), cnsttree(ty, 1UL));
|
||||
} else
|
||||
p = simplify(NEG, ty, p, NULL);
|
||||
} else
|
||||
typeerror(SUB, p, NULL); break;
|
||||
case '~': t = gettok(); p = unary(); p = pointer(p);
|
||||
if (isint(p->type)) {
|
||||
Type ty = promote(p->type);
|
||||
p = simplify(BCOM, ty, cast(p, ty), NULL);
|
||||
} else
|
||||
typeerror(BCOM, p, NULL); break;
|
||||
case '!': t = gettok(); p = unary(); p = pointer(p);
|
||||
if (isscalar(p->type))
|
||||
p = simplify(NOT, inttype, cond(p), NULL);
|
||||
else
|
||||
typeerror(NOT, p, NULL); break;
|
||||
case INCR: t = gettok(); p = unary(); p = incr(INCR, pointer(p), consttree(1, inttype)); break;
|
||||
case DECR: t = gettok(); p = unary(); p = incr(DECR, pointer(p), consttree(1, inttype)); break;
|
||||
case TYPECODE: case SIZEOF: { int op = t;
|
||||
Type ty;
|
||||
p = NULL;
|
||||
t = gettok();
|
||||
if (t == '(') {
|
||||
t = gettok();
|
||||
if (istypename(t, tsym)) {
|
||||
ty = typename();
|
||||
expect(')');
|
||||
} else {
|
||||
p = postfix(expr(')'));
|
||||
ty = p->type;
|
||||
}
|
||||
} else {
|
||||
p = unary();
|
||||
ty = p->type;
|
||||
}
|
||||
assert(ty);
|
||||
if (op == TYPECODE)
|
||||
p = cnsttree(inttype, (long)ty->op);
|
||||
else {
|
||||
if (isfunc(ty) || ty->size == 0)
|
||||
error("invalid type argument `%t' to `sizeof'\n", ty);
|
||||
else if (p && rightkid(p)->op == FIELD)
|
||||
error("`sizeof' applied to a bit field\n");
|
||||
p = cnsttree(unsignedlong, (unsigned long)ty->size);
|
||||
} } break;
|
||||
case '(':
|
||||
t = gettok();
|
||||
if (istypename(t, tsym)) {
|
||||
Type ty, ty1 = typename(), pty;
|
||||
expect(')');
|
||||
ty = unqual(ty1);
|
||||
if (isenum(ty)) {
|
||||
Type ty2 = ty->type;
|
||||
if (isconst(ty1))
|
||||
ty2 = qual(CONST, ty2);
|
||||
if (isvolatile(ty1))
|
||||
ty2 = qual(VOLATILE, ty2);
|
||||
ty1 = ty2;
|
||||
ty = ty->type;
|
||||
}
|
||||
p = pointer(unary());
|
||||
pty = p->type;
|
||||
if (isenum(pty))
|
||||
pty = pty->type;
|
||||
if ((isarith(pty) && isarith(ty))
|
||||
|| (isptr(pty) && isptr(ty))) {
|
||||
explicitCast++;
|
||||
p = cast(p, ty);
|
||||
explicitCast--;
|
||||
} else if ((isptr(pty) && isint(ty))
|
||||
|| (isint(pty) && isptr(ty))) {
|
||||
if (Aflag >= 1 && ty->size < pty->size)
|
||||
warning("conversion from `%t' to `%t' is compiler dependent\n", p->type, ty);
|
||||
|
||||
p = cast(p, ty);
|
||||
} else if (ty != voidtype) {
|
||||
error("cast from `%t' to `%t' is illegal\n",
|
||||
p->type, ty1);
|
||||
ty1 = inttype;
|
||||
}
|
||||
if (generic(p->op) == INDIR || ty->size == 0)
|
||||
p = tree(RIGHT, ty1, NULL, p);
|
||||
else
|
||||
p = retype(p, ty1);
|
||||
} else
|
||||
p = postfix(expr(')'));
|
||||
break;
|
||||
default:
|
||||
p = postfix(primary());
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static Tree postfix(Tree p) {
|
||||
for (;;)
|
||||
switch (t) {
|
||||
case INCR: p = tree(RIGHT, p->type,
|
||||
tree(RIGHT, p->type,
|
||||
p,
|
||||
incr(t, p, consttree(1, inttype))),
|
||||
p);
|
||||
t = gettok(); break;
|
||||
case DECR: p = tree(RIGHT, p->type,
|
||||
tree(RIGHT, p->type,
|
||||
p,
|
||||
incr(t, p, consttree(1, inttype))),
|
||||
p);
|
||||
t = gettok(); break;
|
||||
case '[': {
|
||||
Tree q;
|
||||
t = gettok();
|
||||
q = expr(']');
|
||||
if (YYnull) {
|
||||
if (isptr(p->type))
|
||||
p = nullcheck(p);
|
||||
else if (isptr(q->type))
|
||||
q = nullcheck(q);
|
||||
}
|
||||
p = (*optree['+'])(ADD, pointer(p), pointer(q));
|
||||
if (isptr(p->type) && isarray(p->type->type))
|
||||
p = retype(p, p->type->type);
|
||||
else
|
||||
p = rvalue(p);
|
||||
} break;
|
||||
case '(': {
|
||||
Type ty;
|
||||
Coordinate pt;
|
||||
p = pointer(p);
|
||||
if (isptr(p->type) && isfunc(p->type->type))
|
||||
ty = p->type->type;
|
||||
else {
|
||||
error("found `%t' expected a function\n", p->type);
|
||||
ty = func(voidtype, NULL, 1);
|
||||
p = retype(p, ptr(ty));
|
||||
}
|
||||
pt = src;
|
||||
t = gettok();
|
||||
p = call(p, ty, pt);
|
||||
} break;
|
||||
case '.': t = gettok();
|
||||
if (t == ID) {
|
||||
if (isstruct(p->type)) {
|
||||
Tree q = addrof(p);
|
||||
p = field(q, token);
|
||||
q = rightkid(q);
|
||||
if (isaddrop(q->op) && q->u.sym->temporary)
|
||||
p = tree(RIGHT, p->type, p, NULL);
|
||||
} else
|
||||
error("left operand of . has incompatible type `%t'\n",
|
||||
p->type);
|
||||
t = gettok();
|
||||
} else
|
||||
error("field name expected\n"); break;
|
||||
case DEREF: t = gettok();
|
||||
p = pointer(p);
|
||||
if (t == ID) {
|
||||
if (isptr(p->type) && isstruct(p->type->type)) {
|
||||
if (YYnull)
|
||||
p = nullcheck(p);
|
||||
p = field(p, token);
|
||||
} else
|
||||
error("left operand of -> has incompatible type `%t'\n", p->type);
|
||||
|
||||
t = gettok();
|
||||
} else
|
||||
error("field name expected\n"); break;
|
||||
default:
|
||||
return p;
|
||||
}
|
||||
}
|
||||
static Tree primary(void) {
|
||||
Tree p;
|
||||
|
||||
assert(t != '(');
|
||||
switch (t) {
|
||||
case ICON:
|
||||
case FCON: p = tree(mkop(CNST,tsym->type), tsym->type, NULL, NULL);
|
||||
p->u.v = tsym->u.c.v;
|
||||
break;
|
||||
case SCON: if (ischar(tsym->type->type))
|
||||
tsym->u.c.v.p = stringn(tsym->u.c.v.p, tsym->type->size);
|
||||
else
|
||||
tsym->u.c.v.p = memcpy(allocate((tsym->type->size/widechar->size)*sizeof (int), PERM),
|
||||
tsym->u.c.v.p, (tsym->type->size/widechar->size)*sizeof (int));
|
||||
tsym = constant(tsym->type, tsym->u.c.v);
|
||||
if (tsym->u.c.loc == NULL)
|
||||
tsym->u.c.loc = genident(STATIC, tsym->type, GLOBAL);
|
||||
p = idtree(tsym->u.c.loc); break;
|
||||
case ID: if (tsym == NULL)
|
||||
{
|
||||
Symbol p = install(token, &identifiers, level, PERM);
|
||||
p->src = src;
|
||||
if (getchr() == '(') {
|
||||
Symbol q = lookup(token, externals);
|
||||
p->type = func(inttype, NULL, 1);
|
||||
p->sclass = EXTERN;
|
||||
if (Aflag >= 1)
|
||||
warning("missing prototype\n");
|
||||
if (q && !eqtype(q->type, p->type, 1))
|
||||
warning("implicit declaration of `%s' does not match previous declaration at %w\n", q->name, &q->src);
|
||||
|
||||
if (q == NULL) {
|
||||
q = install(p->name, &externals, GLOBAL, PERM);
|
||||
q->type = p->type;
|
||||
q->sclass = EXTERN;
|
||||
q->src = src;
|
||||
(*IR->defsymbol)(q);
|
||||
}
|
||||
p->u.alias = q;
|
||||
} else {
|
||||
error("undeclared identifier `%s'\n", p->name);
|
||||
p->sclass = AUTO;
|
||||
p->type = inttype;
|
||||
if (p->scope == GLOBAL)
|
||||
(*IR->defsymbol)(p);
|
||||
else
|
||||
addlocal(p);
|
||||
}
|
||||
t = gettok();
|
||||
if (xref)
|
||||
use(p, src);
|
||||
return idtree(p);
|
||||
}
|
||||
if (xref)
|
||||
use(tsym, src);
|
||||
if (tsym->sclass == ENUM)
|
||||
p = consttree(tsym->u.value, inttype);
|
||||
else {
|
||||
if (tsym->sclass == TYPEDEF)
|
||||
error("illegal use of type name `%s'\n", tsym->name);
|
||||
p = idtree(tsym);
|
||||
} break;
|
||||
case FIRSTARG:
|
||||
if (level > PARAM && cfunc && cfunc->u.f.callee[0])
|
||||
p = idtree(cfunc->u.f.callee[0]);
|
||||
else {
|
||||
error("illegal use of `%k'\n", FIRSTARG);
|
||||
p = cnsttree(inttype, 0L);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
error("illegal expression\n");
|
||||
p = cnsttree(inttype, 0L);
|
||||
}
|
||||
t = gettok();
|
||||
return p;
|
||||
}
|
||||
Tree idtree(Symbol p) {
|
||||
int op;
|
||||
Tree e;
|
||||
Type ty = p->type ? unqual(p->type) : voidptype;
|
||||
|
||||
if (p->scope == GLOBAL || p->sclass == STATIC)
|
||||
op = ADDRG;
|
||||
else if (p->scope == PARAM) {
|
||||
op = ADDRF;
|
||||
if (isstruct(p->type) && !IR->wants_argb)
|
||||
{
|
||||
e = tree(mkop(op,voidptype), ptr(ptr(p->type)), NULL, NULL);
|
||||
e->u.sym = p;
|
||||
return rvalue(rvalue(e));
|
||||
}
|
||||
} else if (p->sclass == EXTERN) {
|
||||
assert(p->u.alias);
|
||||
p = p->u.alias;
|
||||
op = ADDRG;
|
||||
} else
|
||||
op = ADDRL;
|
||||
p->ref += refinc;
|
||||
if (isarray(ty))
|
||||
e = tree(mkop(op,voidptype), p->type, NULL, NULL);
|
||||
else if (isfunc(ty))
|
||||
e = tree(mkop(op,funcptype), p->type, NULL, NULL);
|
||||
else
|
||||
e = tree(mkop(op,voidptype), ptr(p->type), NULL, NULL);
|
||||
e->u.sym = p;
|
||||
if (isptr(e->type))
|
||||
e = rvalue(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
Tree rvalue(Tree p) {
|
||||
Type ty = deref(p->type);
|
||||
|
||||
ty = unqual(ty);
|
||||
return tree(mkop(INDIR,ty), ty, p, NULL);
|
||||
}
|
||||
Tree lvalue(Tree p) {
|
||||
if (generic(p->op) != INDIR) {
|
||||
error("lvalue required\n");
|
||||
return value(p);
|
||||
} else if (unqual(p->type) == voidtype)
|
||||
warning("`%t' used as an lvalue\n", p->type);
|
||||
return p->kids[0];
|
||||
}
|
||||
Tree retype(Tree p, Type ty) {
|
||||
Tree q;
|
||||
|
||||
if (p->type == ty)
|
||||
return p;
|
||||
q = tree(p->op, ty, p->kids[0], p->kids[1]);
|
||||
q->node = p->node;
|
||||
q->u = p->u;
|
||||
return q;
|
||||
}
|
||||
Tree rightkid(Tree p) {
|
||||
while (p && p->op == RIGHT)
|
||||
if (p->kids[1])
|
||||
p = p->kids[1];
|
||||
else if (p->kids[0])
|
||||
p = p->kids[0];
|
||||
else
|
||||
assert(0);
|
||||
assert(p);
|
||||
return p;
|
||||
}
|
||||
int hascall(Tree p) {
|
||||
if (p == 0)
|
||||
return 0;
|
||||
if (generic(p->op) == CALL || (IR->mulops_calls &&
|
||||
(p->op == DIV+I || p->op == MOD+I || p->op == MUL+I
|
||||
|| p->op == DIV+U || p->op == MOD+U || p->op == MUL+U)))
|
||||
return 1;
|
||||
return hascall(p->kids[0]) || hascall(p->kids[1]);
|
||||
}
|
||||
Type binary(Type xty, Type yty) {
|
||||
#define xx(t) if (xty == t || yty == t) return t
|
||||
xx(longdouble);
|
||||
xx(doubletype);
|
||||
xx(floattype);
|
||||
xx(unsignedlonglong);
|
||||
xx(longlong);
|
||||
xx(unsignedlong);
|
||||
if ((xty == longtype && yty == unsignedtype)
|
||||
|| (xty == unsignedtype && yty == longtype)) {
|
||||
if (longtype->size > unsignedtype->size)
|
||||
return longtype;
|
||||
else
|
||||
return unsignedlong;
|
||||
}
|
||||
xx(longtype);
|
||||
xx(unsignedtype);
|
||||
return inttype;
|
||||
#undef xx
|
||||
}
|
||||
Tree pointer(Tree p) {
|
||||
if (isarray(p->type))
|
||||
/* assert(p->op != RIGHT || p->u.sym == NULL), */
|
||||
p = retype(p, atop(p->type));
|
||||
else if (isfunc(p->type))
|
||||
p = retype(p, ptr(p->type));
|
||||
return p;
|
||||
}
|
||||
Tree cond(Tree p) {
|
||||
int op = generic(rightkid(p)->op);
|
||||
|
||||
if (op == AND || op == OR || op == NOT
|
||||
|| op == EQ || op == NE
|
||||
|| op == LE || op == LT || op == GE || op == GT)
|
||||
return p;
|
||||
p = pointer(p);
|
||||
return (*optree[NEQ])(NE, p, consttree(0, inttype));
|
||||
}
|
||||
Tree cast(Tree p, Type type) {
|
||||
Type src, dst;
|
||||
|
||||
p = value(p);
|
||||
if (p->type == type)
|
||||
return p;
|
||||
dst = unqual(type);
|
||||
src = unqual(p->type);
|
||||
if (src->op != dst->op || src->size != dst->size) {
|
||||
switch (src->op) {
|
||||
case INT:
|
||||
if (src->size < inttype->size)
|
||||
p = simplify(CVI, inttype, p, NULL);
|
||||
break;
|
||||
case UNSIGNED:
|
||||
if (src->size < inttype->size)
|
||||
p = simplify(CVU, inttype, p, NULL);
|
||||
else if (src->size < unsignedtype->size)
|
||||
p = simplify(CVU, unsignedtype, p, NULL);
|
||||
break;
|
||||
case ENUM:
|
||||
p = retype(p, inttype);
|
||||
break;
|
||||
case POINTER:
|
||||
if (isint(dst) && src->size > dst->size)
|
||||
warning("conversion from `%t' to `%t' is undefined\n", p->type, type);
|
||||
p = simplify(CVP, super(src), p, NULL);
|
||||
break;
|
||||
case FLOAT:
|
||||
break;
|
||||
default: assert(0);
|
||||
}
|
||||
{
|
||||
src = unqual(p->type);
|
||||
dst = super(dst);
|
||||
if (src->op != dst->op)
|
||||
switch (src->op) {
|
||||
case INT:
|
||||
p = simplify(CVI, dst, p, NULL);
|
||||
break;
|
||||
case UNSIGNED:
|
||||
if (isfloat(dst)) {
|
||||
Type ssrc = signedint(src);
|
||||
Tree two = cnsttree(longdouble, (long double)2.0);
|
||||
p = (*optree['+'])(ADD,
|
||||
(*optree['*'])(MUL,
|
||||
two,
|
||||
simplify(CVU, ssrc,
|
||||
simplify(RSH, src,
|
||||
p, consttree(1, inttype)), NULL)),
|
||||
simplify(CVU, ssrc,
|
||||
simplify(BAND, src,
|
||||
p, consttree(1, unsignedtype)), NULL));
|
||||
} else
|
||||
p = simplify(CVU, dst, p, NULL);
|
||||
break;
|
||||
case FLOAT:
|
||||
if (isunsigned(dst)) {
|
||||
Type sdst = signedint(dst);
|
||||
Tree c = cast(cnsttree(longdouble, (long double)sdst->u.sym->u.limits.max.i + 1), src);
|
||||
p = condtree(
|
||||
simplify(GE, src, p, c),
|
||||
(*optree['+'])(ADD,
|
||||
cast(cast(simplify(SUB, src, p, c), sdst), dst),
|
||||
cast(cnsttree(unsignedlong, (unsigned long)sdst->u.sym->u.limits.max.i + 1), dst)),
|
||||
simplify(CVF, sdst, p, NULL));
|
||||
} else
|
||||
p = simplify(CVF, dst, p, NULL);
|
||||
break;
|
||||
default: assert(0);
|
||||
}
|
||||
dst = unqual(type);
|
||||
}
|
||||
}
|
||||
src = unqual(p->type);
|
||||
switch (src->op) {
|
||||
case INT:
|
||||
if (src->op != dst->op || src->size != dst->size)
|
||||
p = simplify(CVI, dst, p, NULL);
|
||||
break;
|
||||
case UNSIGNED:
|
||||
if (src->op != dst->op || src->size != dst->size)
|
||||
p = simplify(CVU, dst, p, NULL);
|
||||
break;
|
||||
case FLOAT:
|
||||
if (src->op != dst->op || src->size != dst->size)
|
||||
p = simplify(CVF, dst, p, NULL);
|
||||
break;
|
||||
case POINTER:
|
||||
if (src->op != dst->op)
|
||||
p = simplify(CVP, dst, p, NULL);
|
||||
else {
|
||||
if ((isfunc(src->type) && !isfunc(dst->type))
|
||||
|| (!isfunc(src->type) && isfunc(dst->type)))
|
||||
warning("conversion from `%t' to `%t' is compiler dependent\n", p->type, type);
|
||||
|
||||
if (src->size != dst->size)
|
||||
p = simplify(CVP, dst, p, NULL);
|
||||
}
|
||||
break;
|
||||
default: assert(0);
|
||||
}
|
||||
return retype(p, type);
|
||||
}
|
||||
Tree field(Tree p, const char *name) {
|
||||
Field q;
|
||||
Type ty1, ty = p->type;
|
||||
|
||||
if (isptr(ty))
|
||||
ty = deref(ty);
|
||||
ty1 = ty;
|
||||
ty = unqual(ty);
|
||||
if ((q = fieldref(name, ty)) != NULL) {
|
||||
if (isarray(q->type)) {
|
||||
ty = q->type->type;
|
||||
if (isconst(ty1) && !isconst(ty))
|
||||
ty = qual(CONST, ty);
|
||||
if (isvolatile(ty1) && !isvolatile(ty))
|
||||
ty = qual(VOLATILE, ty);
|
||||
ty = array(ty, q->type->size/ty->size, q->type->align);
|
||||
} else {
|
||||
ty = q->type;
|
||||
if (isconst(ty1) && !isconst(ty))
|
||||
ty = qual(CONST, ty);
|
||||
if (isvolatile(ty1) && !isvolatile(ty))
|
||||
ty = qual(VOLATILE, ty);
|
||||
ty = ptr(ty);
|
||||
}
|
||||
if (YYcheck && !isaddrop(p->op) && q->offset > 0) /* omit */
|
||||
p = nullcall(ty, YYcheck, p, consttree(q->offset, inttype)); /* omit */
|
||||
else /* omit */
|
||||
p = simplify(ADD+P, ty, p, consttree(q->offset, signedptr));
|
||||
|
||||
if (q->lsb) {
|
||||
p = tree(FIELD, ty->type, rvalue(p), NULL);
|
||||
p->u.field = q;
|
||||
} else if (!isarray(q->type))
|
||||
p = rvalue(p);
|
||||
|
||||
} else {
|
||||
error("unknown field `%s' of `%t'\n", name, ty);
|
||||
p = rvalue(retype(p, ptr(inttype)));
|
||||
}
|
||||
return p;
|
||||
}
|
||||
/* funcname - return name of function f or a function' */
|
||||
char *funcname(Tree f) {
|
||||
if (isaddrop(f->op))
|
||||
return stringf("`%s'", f->u.sym->name);
|
||||
return "a function";
|
||||
}
|
||||
static Tree nullcheck(Tree p) {
|
||||
if (!needconst && YYnull && isptr(p->type)) {
|
||||
p = value(p);
|
||||
if (strcmp(YYnull->name, "_YYnull") == 0) {
|
||||
Symbol t1 = temporary(REGISTER, voidptype);
|
||||
p = tree(RIGHT, p->type,
|
||||
tree(OR, voidtype,
|
||||
cond(asgn(t1, cast(p, voidptype))),
|
||||
vcall(YYnull, voidtype, (file && *file ? pointer(idtree(mkstr(file)->u.c.loc)) : cnsttree(voidptype, NULL)), cnsttree(inttype, (long)lineno) , NULL)),
|
||||
idtree(t1));
|
||||
}
|
||||
|
||||
else
|
||||
p = nullcall(p->type, YYnull, p, cnsttree(inttype, 0L));
|
||||
|
||||
}
|
||||
return p;
|
||||
}
|
||||
Tree nullcall(Type pty, Symbol f, Tree p, Tree e) {
|
||||
Type ty;
|
||||
|
||||
if (isarray(pty))
|
||||
return retype(nullcall(atop(pty), f, p, e), pty);
|
||||
ty = unqual(unqual(p->type)->type);
|
||||
return vcall(f, pty,
|
||||
p, e,
|
||||
cnsttree(inttype, (long)ty->size),
|
||||
cnsttree(inttype, (long)ty->align),
|
||||
(file && *file ? pointer(idtree(mkstr(file)->u.c.loc)) : cnsttree(voidptype, NULL)), cnsttree(inttype, (long)lineno) , NULL);
|
||||
}
|
||||
828
src/cmd/lccom-1/gen.c
Normal file
828
src/cmd/lccom-1/gen.c
Normal file
@@ -0,0 +1,828 @@
|
||||
#include "c.h"
|
||||
|
||||
#define readsreg(p) \
|
||||
(generic((p)->op)==INDIR && (p)->kids[0]->op==VREG+P)
|
||||
#define setsrc(d) ((d) && (d)->x.regnode && \
|
||||
(d)->x.regnode->set == src->x.regnode->set && \
|
||||
(d)->x.regnode->mask&src->x.regnode->mask)
|
||||
|
||||
#define relink(a, b) ((b)->x.prev = (a), (a)->x.next = (b))
|
||||
|
||||
static Symbol askfixedreg(Symbol);
|
||||
static Symbol askreg(Symbol, unsigned*);
|
||||
static void blkunroll(int, int, int, int, int, int, int[]);
|
||||
static void docall(Node);
|
||||
static void dumpcover(Node, int, int);
|
||||
static void dumpregs(char *, char *, char *);
|
||||
static void dumprule(int);
|
||||
static void dumptree(Node);
|
||||
static void genreload(Node, Symbol, int);
|
||||
static void genspill(Symbol, Node, Symbol);
|
||||
static Symbol getreg(Symbol, unsigned*, Node);
|
||||
static int getrule(Node, int);
|
||||
static void linearize(Node, Node);
|
||||
static int moveself(Node);
|
||||
static void prelabel(Node);
|
||||
static Node* prune(Node, Node*);
|
||||
static void putreg(Symbol);
|
||||
static void ralloc(Node);
|
||||
static void reduce(Node, int);
|
||||
static int reprune(Node*, int, int, Node);
|
||||
static int requate(Node);
|
||||
static Node reuse(Node, int);
|
||||
static void rewrite(Node);
|
||||
static Symbol spillee(Symbol, unsigned mask[], Node);
|
||||
static void spillr(Symbol, Node);
|
||||
static int uses(Node, Regnode);
|
||||
|
||||
int offset;
|
||||
|
||||
int maxoffset;
|
||||
|
||||
int framesize;
|
||||
int argoffset;
|
||||
|
||||
int maxargoffset;
|
||||
|
||||
int dalign, salign;
|
||||
int bflag = 0; /* omit */
|
||||
int dflag = 0;
|
||||
|
||||
int swap;
|
||||
|
||||
unsigned (*emitter)(Node, int) = emitasm;
|
||||
static char NeedsReg[] = {
|
||||
0, /* unused */
|
||||
1, /* CNST */
|
||||
0, 0, /* ARG ASGN */
|
||||
1, /* INDIR */
|
||||
0, 0, 1, 1, /* - - CVF CVI */
|
||||
1, 0, 1, 1, /* CVP - CVU NEG */
|
||||
1, /* CALL */
|
||||
1, /* LOAD */
|
||||
0, /* RET */
|
||||
1, 1, 1, /* ADDRG ADDRF ADDRL */
|
||||
1, 1, 1, 1, 1, /* ADD SUB LSH MOD RSH */
|
||||
1, 1, 1, 1, /* BAND BCOM BOR BXOR */
|
||||
1, 1, /* DIV MUL */
|
||||
0, 0, 0, 0, 0, 0, /* EQ GE GT LE LT NE */
|
||||
0, 0 /* JUMP LABEL */
|
||||
};
|
||||
Node head;
|
||||
|
||||
unsigned freemask[2];
|
||||
unsigned usedmask[2];
|
||||
unsigned tmask[2];
|
||||
unsigned vmask[2];
|
||||
Symbol mkreg(char *fmt, int n, int mask, int set) {
|
||||
Symbol p;
|
||||
|
||||
NEW0(p, PERM);
|
||||
p->name = p->x.name = stringf(fmt, n);
|
||||
NEW0(p->x.regnode, PERM);
|
||||
p->x.regnode->number = n;
|
||||
p->x.regnode->mask = mask<<n;
|
||||
p->x.regnode->set = set;
|
||||
return p;
|
||||
}
|
||||
Symbol mkwildcard(Symbol *syms) {
|
||||
Symbol p;
|
||||
|
||||
NEW0(p, PERM);
|
||||
p->name = p->x.name = "wildcard";
|
||||
p->x.wildcard = syms;
|
||||
return p;
|
||||
}
|
||||
void mkauto(Symbol p) {
|
||||
assert(p->sclass == AUTO);
|
||||
offset = roundup(offset + p->type->size, p->type->align);
|
||||
p->x.offset = -offset;
|
||||
p->x.name = stringd(-offset);
|
||||
}
|
||||
void blockbeg(Env *e) {
|
||||
e->offset = offset;
|
||||
e->freemask[IREG] = freemask[IREG];
|
||||
e->freemask[FREG] = freemask[FREG];
|
||||
}
|
||||
void blockend(Env *e) {
|
||||
if (offset > maxoffset)
|
||||
maxoffset = offset;
|
||||
offset = e->offset;
|
||||
freemask[IREG] = e->freemask[IREG];
|
||||
freemask[FREG] = e->freemask[FREG];
|
||||
}
|
||||
int mkactual(int align, int size) {
|
||||
int n = roundup(argoffset, align);
|
||||
|
||||
argoffset = n + size;
|
||||
return n;
|
||||
}
|
||||
static void docall(Node p) {
|
||||
p->syms[1] = p->syms[0];
|
||||
p->syms[0] = intconst(argoffset);
|
||||
if (argoffset > maxargoffset)
|
||||
maxargoffset = argoffset;
|
||||
argoffset = 0;
|
||||
}
|
||||
void blkcopy(int dreg, int doff, int sreg, int soff, int size, int tmp[]) {
|
||||
assert(size >= 0);
|
||||
if (size == 0)
|
||||
return;
|
||||
else if (size <= 2)
|
||||
blkunroll(size, dreg, doff, sreg, soff, size, tmp);
|
||||
else if (size == 3) {
|
||||
blkunroll(2, dreg, doff, sreg, soff, 2, tmp);
|
||||
blkunroll(1, dreg, doff+2, sreg, soff+2, 1, tmp);
|
||||
}
|
||||
else if (size <= 16) {
|
||||
blkunroll(4, dreg, doff, sreg, soff, size&~3, tmp);
|
||||
blkcopy(dreg, doff+(size&~3),
|
||||
sreg, soff+(size&~3), size&3, tmp);
|
||||
}
|
||||
else
|
||||
(*IR->x.blkloop)(dreg, doff, sreg, soff, size, tmp);
|
||||
}
|
||||
static void blkunroll(int k, int dreg, int doff, int sreg, int soff, int size, int tmp[]) {
|
||||
int i;
|
||||
|
||||
assert(IR->x.max_unaligned_load);
|
||||
if (k > IR->x.max_unaligned_load
|
||||
&& (k > salign || k > dalign))
|
||||
k = IR->x.max_unaligned_load;
|
||||
for (i = 0; i+k < size; i += 2*k) {
|
||||
(*IR->x.blkfetch)(k, soff+i, sreg, tmp[0]);
|
||||
(*IR->x.blkfetch)(k, soff+i+k, sreg, tmp[1]);
|
||||
(*IR->x.blkstore)(k, doff+i, dreg, tmp[0]);
|
||||
(*IR->x.blkstore)(k, doff+i+k, dreg, tmp[1]);
|
||||
}
|
||||
if (i < size) {
|
||||
(*IR->x.blkfetch)(k, i+soff, sreg, tmp[0]);
|
||||
(*IR->x.blkstore)(k, i+doff, dreg, tmp[0]);
|
||||
}
|
||||
}
|
||||
void parseflags(int argc, char *argv[]) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < argc; i++)
|
||||
if (strcmp(argv[i], "-d") == 0)
|
||||
dflag = 1;
|
||||
else if (strcmp(argv[i], "-b") == 0) /* omit */
|
||||
bflag = 1; /* omit */
|
||||
}
|
||||
static int getrule(Node p, int nt) {
|
||||
int rulenum;
|
||||
|
||||
assert(p);
|
||||
rulenum = (*IR->x._rule)(p->x.state, nt);
|
||||
if (!rulenum) {
|
||||
fprint(stderr, "(%x->op=%s at %w is corrupt.)\n", p, opname(p->op), &src);
|
||||
assert(0);
|
||||
}
|
||||
return rulenum;
|
||||
}
|
||||
static void reduce(Node p, int nt) {
|
||||
int rulenum, i;
|
||||
short *nts;
|
||||
Node kids[10];
|
||||
|
||||
p = reuse(p, nt);
|
||||
rulenum = getrule(p, nt);
|
||||
nts = IR->x._nts[rulenum];
|
||||
(*IR->x._kids)(p, rulenum, kids);
|
||||
for (i = 0; nts[i]; i++)
|
||||
reduce(kids[i], nts[i]);
|
||||
if (IR->x._isinstruction[rulenum]) {
|
||||
assert(p->x.inst == 0 || p->x.inst == nt);
|
||||
p->x.inst = nt;
|
||||
if (p->syms[RX] && p->syms[RX]->temporary) {
|
||||
debug(fprint(stderr, "(using %s)\n", p->syms[RX]->name));
|
||||
p->syms[RX]->x.usecount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
static Node reuse(Node p, int nt) {
|
||||
struct _state {
|
||||
short cost[1];
|
||||
};
|
||||
Symbol r = p->syms[RX];
|
||||
|
||||
if (generic(p->op) == INDIR && p->kids[0]->op == VREG+P
|
||||
&& r->u.t.cse && p->x.mayrecalc
|
||||
&& ((struct _state*)r->u.t.cse->x.state)->cost[nt] == 0)
|
||||
return r->u.t.cse;
|
||||
else
|
||||
return p;
|
||||
}
|
||||
|
||||
int mayrecalc(Node p) {
|
||||
int op;
|
||||
|
||||
assert(p && p->syms[RX]);
|
||||
if (p->syms[RX]->u.t.cse == NULL)
|
||||
return 0;
|
||||
op = generic(p->syms[RX]->u.t.cse->op);
|
||||
if (op == CNST || op == ADDRF || op == ADDRG || op == ADDRL) {
|
||||
p->x.mayrecalc = 1;
|
||||
return 1;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
static Node *prune(Node p, Node pp[]) {
|
||||
if (p == NULL)
|
||||
return pp;
|
||||
p->x.kids[0] = p->x.kids[1] = p->x.kids[2] = NULL;
|
||||
if (p->x.inst == 0)
|
||||
return prune(p->kids[1], prune(p->kids[0], pp));
|
||||
else if (p->syms[RX] && p->syms[RX]->temporary
|
||||
&& p->syms[RX]->x.usecount < 2) {
|
||||
p->x.inst = 0;
|
||||
debug(fprint(stderr, "(clobbering %s)\n", p->syms[RX]->name));
|
||||
return prune(p->kids[1], prune(p->kids[0], pp));
|
||||
}
|
||||
else {
|
||||
prune(p->kids[1], prune(p->kids[0], &p->x.kids[0]));
|
||||
*pp = p;
|
||||
return pp + 1;
|
||||
}
|
||||
}
|
||||
|
||||
#define ck(i) return (i) ? 0 : LBURG_MAX
|
||||
|
||||
int range(Node p, int lo, int hi) {
|
||||
Symbol s = p->syms[0];
|
||||
|
||||
switch (specific(p->op)) {
|
||||
case ADDRF+P:
|
||||
case ADDRL+P: ck(s->x.offset >= lo && s->x.offset <= hi);
|
||||
case CNST+I: ck(s->u.c.v.i >= lo && s->u.c.v.i <= hi);
|
||||
case CNST+U: ck(s->u.c.v.u >= lo && s->u.c.v.u <= hi);
|
||||
case CNST+P: ck(s->u.c.v.p == 0 && lo <= 0 && hi >= 0);
|
||||
}
|
||||
return LBURG_MAX;
|
||||
}
|
||||
static void dumptree(Node p) {
|
||||
if (p->op == VREG+P && p->syms[0]) {
|
||||
fprint(stderr, "VREGP(%s)", p->syms[0]->name);
|
||||
return;
|
||||
} else if (generic(p->op) == LOAD) {
|
||||
fprint(stderr, "LOAD(");
|
||||
dumptree(p->kids[0]);
|
||||
fprint(stderr, ")");
|
||||
return;
|
||||
}
|
||||
fprint(stderr, "%s(", opname(p->op));
|
||||
switch (generic(p->op)) {
|
||||
case CNST: case LABEL:
|
||||
case ADDRG: case ADDRF: case ADDRL:
|
||||
if (p->syms[0])
|
||||
fprint(stderr, "%s", p->syms[0]->name);
|
||||
break;
|
||||
case RET:
|
||||
if (p->kids[0])
|
||||
dumptree(p->kids[0]);
|
||||
break;
|
||||
case CVF: case CVI: case CVP: case CVU: case JUMP:
|
||||
case ARG: case BCOM: case NEG: case INDIR:
|
||||
dumptree(p->kids[0]);
|
||||
break;
|
||||
case CALL:
|
||||
if (optype(p->op) != B) {
|
||||
dumptree(p->kids[0]);
|
||||
break;
|
||||
}
|
||||
/* else fall thru */
|
||||
case EQ: case NE: case GT: case GE: case LE: case LT:
|
||||
case ASGN: case BOR: case BAND: case BXOR: case RSH: case LSH:
|
||||
case ADD: case SUB: case DIV: case MUL: case MOD:
|
||||
dumptree(p->kids[0]);
|
||||
fprint(stderr, ", ");
|
||||
dumptree(p->kids[1]);
|
||||
break;
|
||||
default: assert(0);
|
||||
}
|
||||
fprint(stderr, ")");
|
||||
}
|
||||
static void dumpcover(Node p, int nt, int in) {
|
||||
int rulenum, i;
|
||||
short *nts;
|
||||
Node kids[10];
|
||||
|
||||
p = reuse(p, nt);
|
||||
rulenum = getrule(p, nt);
|
||||
nts = IR->x._nts[rulenum];
|
||||
fprint(stderr, "dumpcover(%x) = ", p);
|
||||
for (i = 0; i < in; i++)
|
||||
fprint(stderr, " ");
|
||||
dumprule(rulenum);
|
||||
(*IR->x._kids)(p, rulenum, kids);
|
||||
for (i = 0; nts[i]; i++)
|
||||
dumpcover(kids[i], nts[i], in+1);
|
||||
}
|
||||
|
||||
static void dumprule(int rulenum) {
|
||||
assert(rulenum);
|
||||
fprint(stderr, "%s / %s", IR->x._string[rulenum],
|
||||
IR->x._templates[rulenum]);
|
||||
if (!IR->x._isinstruction[rulenum])
|
||||
fprint(stderr, "\n");
|
||||
}
|
||||
unsigned emitasm(Node p, int nt) {
|
||||
int rulenum;
|
||||
short *nts;
|
||||
char *fmt;
|
||||
Node kids[10];
|
||||
|
||||
p = reuse(p, nt);
|
||||
rulenum = getrule(p, nt);
|
||||
nts = IR->x._nts[rulenum];
|
||||
fmt = IR->x._templates[rulenum];
|
||||
assert(fmt);
|
||||
if (IR->x._isinstruction[rulenum] && p->x.emitted)
|
||||
print("%s", p->syms[RX]->x.name);
|
||||
else if (*fmt == '#')
|
||||
(*IR->x.emit2)(p);
|
||||
else {
|
||||
if (*fmt == '?') {
|
||||
fmt++;
|
||||
assert(p->kids[0]);
|
||||
if (p->syms[RX] == p->x.kids[0]->syms[RX])
|
||||
while (*fmt++ != '\n')
|
||||
;
|
||||
}
|
||||
for ((*IR->x._kids)(p, rulenum, kids); *fmt; fmt++)
|
||||
if (*fmt != '%')
|
||||
(void)putchar(*fmt);
|
||||
else if (*++fmt == 'F')
|
||||
print("%d", framesize);
|
||||
else if (*fmt >= '0' && *fmt <= '9')
|
||||
emitasm(kids[*fmt - '0'], nts[*fmt - '0']);
|
||||
else if (*fmt >= 'a' && *fmt < 'a' + NELEMS(p->syms))
|
||||
fputs(p->syms[*fmt - 'a']->x.name, stdout);
|
||||
else
|
||||
(void)putchar(*fmt);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void emit(Node p) {
|
||||
for (; p; p = p->x.next) {
|
||||
assert(p->x.registered);
|
||||
if ((p->x.equatable && requate(p)) || moveself(p))
|
||||
;
|
||||
else
|
||||
(*emitter)(p, p->x.inst);
|
||||
p->x.emitted = 1;
|
||||
}
|
||||
}
|
||||
static int moveself(Node p) {
|
||||
return p->x.copy
|
||||
&& p->syms[RX]->x.name == p->x.kids[0]->syms[RX]->x.name;
|
||||
}
|
||||
int move(Node p) {
|
||||
p->x.copy = 1;
|
||||
return 1;
|
||||
}
|
||||
static int requate(Node q) {
|
||||
Symbol src = q->x.kids[0]->syms[RX];
|
||||
Symbol tmp = q->syms[RX];
|
||||
Node p;
|
||||
int n = 0;
|
||||
|
||||
debug(fprint(stderr, "(requate(%x): tmp=%s src=%s)\n", q, tmp->x.name, src->x.name));
|
||||
for (p = q->x.next; p; p = p->x.next)
|
||||
if (p->x.copy && p->syms[RX] == src
|
||||
&& p->x.kids[0]->syms[RX] == tmp)
|
||||
debug(fprint(stderr, "(requate arm 0 at %x)\n", p)),
|
||||
p->syms[RX] = tmp;
|
||||
else if (setsrc(p->syms[RX]) && !moveself(p) && !readsreg(p))
|
||||
return 0;
|
||||
else if (p->x.spills)
|
||||
return 0;
|
||||
else if (generic(p->op) == CALL && p->x.next)
|
||||
return 0;
|
||||
else if (p->op == LABEL+V && p->x.next)
|
||||
return 0;
|
||||
else if (p->syms[RX] == tmp && readsreg(p))
|
||||
debug(fprint(stderr, "(requate arm 5 at %x)\n", p)),
|
||||
n++;
|
||||
else if (p->syms[RX] == tmp)
|
||||
break;
|
||||
debug(fprint(stderr, "(requate arm 7 at %x)\n", p));
|
||||
assert(n > 0);
|
||||
for (p = q->x.next; p; p = p->x.next)
|
||||
if (p->syms[RX] == tmp && readsreg(p)) {
|
||||
p->syms[RX] = src;
|
||||
if (--n <= 0)
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
static void prelabel(Node p) {
|
||||
if (p == NULL)
|
||||
return;
|
||||
prelabel(p->kids[0]);
|
||||
prelabel(p->kids[1]);
|
||||
if (NeedsReg[opindex(p->op)])
|
||||
setreg(p, (*IR->x.rmap)(opkind(p->op)));
|
||||
switch (generic(p->op)) {
|
||||
case ADDRF: case ADDRL:
|
||||
if (p->syms[0]->sclass == REGISTER)
|
||||
p->op = VREG+P;
|
||||
break;
|
||||
case INDIR:
|
||||
if (p->kids[0]->op == VREG+P)
|
||||
setreg(p, p->kids[0]->syms[0]);
|
||||
break;
|
||||
case ASGN:
|
||||
if (p->kids[0]->op == VREG+P)
|
||||
rtarget(p, 1, p->kids[0]->syms[0]);
|
||||
break;
|
||||
case CVI: case CVU: case CVP:
|
||||
if (optype(p->op) != F
|
||||
&& opsize(p->op) <= p->syms[0]->u.c.v.i)
|
||||
p->op = LOAD + opkind(p->op);
|
||||
break;
|
||||
}
|
||||
(IR->x.target)(p);
|
||||
}
|
||||
void setreg(Node p, Symbol r) {
|
||||
p->syms[RX] = r;
|
||||
}
|
||||
void rtarget(Node p, int n, Symbol r) {
|
||||
Node q = p->kids[n];
|
||||
|
||||
assert(q);
|
||||
assert(r);
|
||||
assert(r->sclass == REGISTER || !r->x.wildcard);
|
||||
assert(q->syms[RX]);
|
||||
if (r != q->syms[RX] && !q->syms[RX]->x.wildcard) {
|
||||
q = newnode(LOAD + opkind(q->op),
|
||||
q, NULL, q->syms[0]);
|
||||
if (r->u.t.cse == p->kids[n])
|
||||
r->u.t.cse = q;
|
||||
p->kids[n] = p->x.kids[n] = q;
|
||||
q->x.kids[0] = q->kids[0];
|
||||
}
|
||||
setreg(q, r);
|
||||
debug(fprint(stderr, "(targeting %x->x.kids[%d]=%x to %s)\n", p, n, p->kids[n], r->x.name));
|
||||
}
|
||||
static void rewrite(Node p) {
|
||||
assert(p->x.inst == 0);
|
||||
prelabel(p);
|
||||
debug(dumptree(p));
|
||||
debug(fprint(stderr, "\n"));
|
||||
(*IR->x._label)(p);
|
||||
debug(dumpcover(p, 1, 0));
|
||||
reduce(p, 1);
|
||||
}
|
||||
Node gen(Node forest) {
|
||||
int i;
|
||||
struct node sentinel;
|
||||
Node dummy, p;
|
||||
|
||||
head = forest;
|
||||
for (p = forest; p; p = p->link) {
|
||||
assert(p->count == 0);
|
||||
if (generic(p->op) == CALL)
|
||||
docall(p);
|
||||
else if ( generic(p->op) == ASGN
|
||||
&& generic(p->kids[1]->op) == CALL)
|
||||
docall(p->kids[1]);
|
||||
else if (generic(p->op) == ARG)
|
||||
(*IR->x.doarg)(p);
|
||||
rewrite(p);
|
||||
p->x.listed = 1;
|
||||
}
|
||||
for (p = forest; p; p = p->link)
|
||||
prune(p, &dummy);
|
||||
relink(&sentinel, &sentinel);
|
||||
for (p = forest; p; p = p->link)
|
||||
linearize(p, &sentinel);
|
||||
forest = sentinel.x.next;
|
||||
assert(forest);
|
||||
sentinel.x.next->x.prev = NULL;
|
||||
sentinel.x.prev->x.next = NULL;
|
||||
for (p = forest; p; p = p->x.next)
|
||||
for (i = 0; i < NELEMS(p->x.kids) && p->x.kids[i]; i++) {
|
||||
assert(p->x.kids[i]->syms[RX]);
|
||||
if (p->x.kids[i]->syms[RX]->temporary) {
|
||||
p->x.kids[i]->x.prevuse =
|
||||
p->x.kids[i]->syms[RX]->x.lastuse;
|
||||
p->x.kids[i]->syms[RX]->x.lastuse = p->x.kids[i];
|
||||
}
|
||||
}
|
||||
for (p = forest; p; p = p->x.next) {
|
||||
ralloc(p);
|
||||
if (p->x.listed && NeedsReg[opindex(p->op)]
|
||||
&& (*IR->x.rmap)(opkind(p->op))) {
|
||||
assert(generic(p->op) == CALL || generic(p->op) == LOAD);
|
||||
putreg(p->syms[RX]);
|
||||
}
|
||||
}
|
||||
return forest;
|
||||
}
|
||||
int notarget(Node p) {
|
||||
return p->syms[RX]->x.wildcard ? 0 : LBURG_MAX;
|
||||
}
|
||||
static void putreg(Symbol r) {
|
||||
assert(r && r->x.regnode);
|
||||
freemask[r->x.regnode->set] |= r->x.regnode->mask;
|
||||
debug(dumpregs("(freeing %s)\n", r->x.name, NULL));
|
||||
}
|
||||
static Symbol askfixedreg(Symbol s) {
|
||||
Regnode r = s->x.regnode;
|
||||
int n = r->set;
|
||||
|
||||
if (r->mask&~freemask[n])
|
||||
return NULL;
|
||||
else {
|
||||
freemask[n] &= ~r->mask;
|
||||
usedmask[n] |= r->mask;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
static Symbol askreg(Symbol rs, unsigned rmask[]) {
|
||||
int i;
|
||||
|
||||
if (rs->x.wildcard == NULL)
|
||||
return askfixedreg(rs);
|
||||
for (i = 31; i >= 0; i--) {
|
||||
Symbol r = rs->x.wildcard[i];
|
||||
if (r != NULL
|
||||
&& !(r->x.regnode->mask&~rmask[r->x.regnode->set])
|
||||
&& askfixedreg(r))
|
||||
return r;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static Symbol getreg(Symbol s, unsigned mask[], Node p) {
|
||||
Symbol r = askreg(s, mask);
|
||||
if (r == NULL) {
|
||||
r = spillee(s, mask, p);
|
||||
assert(r && r->x.regnode);
|
||||
spill(r->x.regnode->mask, r->x.regnode->set, p);
|
||||
r = askreg(s, mask);
|
||||
}
|
||||
assert(r && r->x.regnode);
|
||||
r->x.regnode->vbl = NULL;
|
||||
return r;
|
||||
}
|
||||
int askregvar(Symbol p, Symbol regs) {
|
||||
Symbol r;
|
||||
|
||||
assert(p);
|
||||
if (p->sclass != REGISTER)
|
||||
return 0;
|
||||
else if (!isscalar(p->type)) {
|
||||
p->sclass = AUTO;
|
||||
return 0;
|
||||
}
|
||||
else if (p->temporary) {
|
||||
p->x.name = "?";
|
||||
return 1;
|
||||
}
|
||||
else if ((r = askreg(regs, vmask)) != NULL) {
|
||||
p->x.regnode = r->x.regnode;
|
||||
p->x.regnode->vbl = p;
|
||||
p->x.name = r->x.name;
|
||||
debug(dumpregs("(allocating %s to symbol %s)\n", p->x.name, p->name));
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
p->sclass = AUTO;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
static void linearize(Node p, Node next) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NELEMS(p->x.kids) && p->x.kids[i]; i++)
|
||||
linearize(p->x.kids[i], next);
|
||||
relink(next->x.prev, p);
|
||||
relink(p, next);
|
||||
debug(fprint(stderr, "(listing %x)\n", p));
|
||||
}
|
||||
static void ralloc(Node p) {
|
||||
int i;
|
||||
unsigned mask[2];
|
||||
|
||||
mask[0] = tmask[0];
|
||||
mask[1] = tmask[1];
|
||||
assert(p);
|
||||
debug(fprint(stderr, "(rallocing %x)\n", p));
|
||||
for (i = 0; i < NELEMS(p->x.kids) && p->x.kids[i]; i++) {
|
||||
Node kid = p->x.kids[i];
|
||||
Symbol r = kid->syms[RX];
|
||||
assert(r && kid->x.registered);
|
||||
if (r->sclass != REGISTER && r->x.lastuse == kid)
|
||||
putreg(r);
|
||||
}
|
||||
if (!p->x.registered && NeedsReg[opindex(p->op)]
|
||||
&& (*IR->x.rmap)(opkind(p->op))) {
|
||||
Symbol sym = p->syms[RX], set = sym;
|
||||
assert(sym);
|
||||
if (sym->temporary)
|
||||
set = (*IR->x.rmap)(opkind(p->op));
|
||||
assert(set);
|
||||
if (set->sclass != REGISTER) {
|
||||
Symbol r;
|
||||
if (*IR->x._templates[getrule(p, p->x.inst)] == '?')
|
||||
for (i = 1; i < NELEMS(p->x.kids) && p->x.kids[i]; i++) {
|
||||
Symbol r = p->x.kids[i]->syms[RX];
|
||||
assert(p->x.kids[i]->x.registered);
|
||||
assert(r && r->x.regnode);
|
||||
assert(sym->x.wildcard || sym != r);
|
||||
mask[r->x.regnode->set] &= ~r->x.regnode->mask;
|
||||
}
|
||||
r = getreg(set, mask, p);
|
||||
if (sym->temporary) {
|
||||
Node q;
|
||||
r->x.lastuse = sym->x.lastuse;
|
||||
for (q = sym->x.lastuse; q; q = q->x.prevuse) {
|
||||
q->syms[RX] = r;
|
||||
q->x.registered = 1;
|
||||
if (sym->u.t.cse && q->x.copy)
|
||||
q->x.equatable = 1;
|
||||
}
|
||||
} else {
|
||||
p->syms[RX] = r;
|
||||
r->x.lastuse = p;
|
||||
}
|
||||
debug(dumpregs("(allocating %s to node %x)\n", r->x.name, (char *) p));
|
||||
}
|
||||
}
|
||||
p->x.registered = 1;
|
||||
(*IR->x.clobber)(p);
|
||||
}
|
||||
static Symbol spillee(Symbol set, unsigned mask[], Node here) {
|
||||
Symbol bestreg = NULL;
|
||||
int bestdist = -1, i;
|
||||
|
||||
assert(set);
|
||||
if (!set->x.wildcard)
|
||||
bestreg = set;
|
||||
else {
|
||||
for (i = 31; i >= 0; i--) {
|
||||
Symbol ri = set->x.wildcard[i];
|
||||
if (
|
||||
ri != NULL &&
|
||||
ri->x.lastuse &&
|
||||
(ri->x.regnode->mask&tmask[ri->x.regnode->set]&mask[ri->x.regnode->set])
|
||||
) {
|
||||
Regnode rn = ri->x.regnode;
|
||||
Node q = here;
|
||||
int dist = 0;
|
||||
for (; q && !uses(q, rn); q = q->x.next)
|
||||
dist++;
|
||||
if (q && dist > bestdist) {
|
||||
bestdist = dist;
|
||||
bestreg = ri;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(bestreg); /* Must be able to spill something. Reconfigure the register allocator
|
||||
to ensure that we can allocate a register for all nodes without spilling
|
||||
the node's necessary input regs. */
|
||||
assert(bestreg->x.regnode->vbl == NULL); /* Can't spill register variables because
|
||||
the reload site might be in other blocks. Reconfigure the register allocator
|
||||
to ensure that this register is never allocated to a variable. */
|
||||
return bestreg;
|
||||
}
|
||||
static int uses(Node p, Regnode rn) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NELEMS(p->x.kids); i++)
|
||||
if (
|
||||
p->x.kids[i] &&
|
||||
p->x.kids[i]->x.registered &&
|
||||
rn->set == p->x.kids[i]->syms[RX]->x.regnode->set &&
|
||||
(rn->mask&p->x.kids[i]->syms[RX]->x.regnode->mask)
|
||||
)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
static void spillr(Symbol r, Node here) {
|
||||
int i;
|
||||
Symbol tmp;
|
||||
Node p = r->x.lastuse;
|
||||
assert(p);
|
||||
while (p->x.prevuse) {
|
||||
assert(r == p->syms[RX]);
|
||||
p = p->x.prevuse;
|
||||
}
|
||||
assert(p->x.registered && !readsreg(p));
|
||||
tmp = newtemp(AUTO, optype(p->op), opsize(p->op));
|
||||
genspill(r, p, tmp);
|
||||
for (p = here->x.next; p; p = p->x.next)
|
||||
for (i = 0; i < NELEMS(p->x.kids) && p->x.kids[i]; i++) {
|
||||
Node k = p->x.kids[i];
|
||||
if (k->x.registered && k->syms[RX] == r)
|
||||
genreload(p, tmp, i);
|
||||
}
|
||||
putreg(r);
|
||||
}
|
||||
static void genspill(Symbol r, Node last, Symbol tmp) {
|
||||
Node p, q;
|
||||
Symbol s;
|
||||
unsigned ty;
|
||||
|
||||
debug(fprint(stderr, "(spilling %s to local %s)\n", r->x.name, tmp->x.name));
|
||||
debug(fprint(stderr, "(genspill: "));
|
||||
debug(dumptree(last));
|
||||
debug(fprint(stderr, ")\n"));
|
||||
ty = opkind(last->op);
|
||||
NEW0(s, FUNC);
|
||||
s->sclass = REGISTER;
|
||||
s->name = s->x.name = r->x.name;
|
||||
s->x.regnode = r->x.regnode;
|
||||
q = newnode(ADDRL+P + sizeop(IR->ptrmetric.size), NULL, NULL, s);
|
||||
q = newnode(INDIR + ty, q, NULL, NULL);
|
||||
p = newnode(ADDRL+P + sizeop(IR->ptrmetric.size), NULL, NULL, tmp);
|
||||
p = newnode(ASGN + ty, p, q, NULL);
|
||||
p->x.spills = 1;
|
||||
rewrite(p);
|
||||
prune(p, &q);
|
||||
q = last->x.next;
|
||||
linearize(p, q);
|
||||
for (p = last->x.next; p != q; p = p->x.next) {
|
||||
ralloc(p);
|
||||
assert(!p->x.listed || !NeedsReg[opindex(p->op)] || !(*IR->x.rmap)(opkind(p->op)));
|
||||
}
|
||||
}
|
||||
|
||||
static void genreload(Node p, Symbol tmp, int i) {
|
||||
Node q;
|
||||
int ty;
|
||||
|
||||
debug(fprint(stderr, "(replacing %x with a reload from %s)\n", p->x.kids[i], tmp->x.name));
|
||||
debug(fprint(stderr, "(genreload: "));
|
||||
debug(dumptree(p->x.kids[i]));
|
||||
debug(fprint(stderr, ")\n"));
|
||||
ty = opkind(p->x.kids[i]->op);
|
||||
q = newnode(ADDRL+P + sizeop(IR->ptrmetric.size), NULL, NULL, tmp);
|
||||
p->x.kids[i] = newnode(INDIR + ty, q, NULL, NULL);
|
||||
rewrite(p->x.kids[i]);
|
||||
prune(p->x.kids[i], &q);
|
||||
reprune(&p->kids[1], reprune(&p->kids[0], 0, i, p), i, p);
|
||||
prune(p, &q);
|
||||
linearize(p->x.kids[i], p);
|
||||
}
|
||||
static int reprune(Node *pp, int k, int n, Node p) {
|
||||
struct node x, *q = *pp;
|
||||
|
||||
if (q == NULL || k > n)
|
||||
return k;
|
||||
else if (q->x.inst == 0)
|
||||
return reprune(&q->kids[1],
|
||||
reprune(&q->kids[0], k, n, p), n, p);
|
||||
if (k == n) {
|
||||
debug(fprint(stderr, "(reprune changes %x from %x to %x)\n", pp, *pp, p->x.kids[n]));
|
||||
*pp = p->x.kids[n];
|
||||
x = *p;
|
||||
(IR->x.target)(&x);
|
||||
}
|
||||
return k + 1;
|
||||
}
|
||||
void spill(unsigned mask, int n, Node here) {
|
||||
int i;
|
||||
Node p;
|
||||
|
||||
here->x.spills = 1;
|
||||
usedmask[n] |= mask;
|
||||
if (mask&~freemask[n]) {
|
||||
|
||||
assert( /* It makes no sense for a node to clobber() its target. */
|
||||
here->x.registered == 0 || /* call isn't coming through clobber() */
|
||||
here->syms[RX] == NULL ||
|
||||
here->syms[RX]->x.regnode == NULL ||
|
||||
here->syms[RX]->x.regnode->set != n ||
|
||||
(here->syms[RX]->x.regnode->mask&mask) == 0
|
||||
);
|
||||
|
||||
for (p = here; p; p = p->x.next)
|
||||
for (i = 0; i < NELEMS(p->x.kids) && p->x.kids[i]; i++) {
|
||||
Symbol r = p->x.kids[i]->syms[RX];
|
||||
assert(r);
|
||||
if (p->x.kids[i]->x.registered && r->x.regnode->set == n
|
||||
&& r->x.regnode->mask&mask)
|
||||
spillr(r, here);
|
||||
}
|
||||
}
|
||||
}
|
||||
static void dumpregs(char *msg, char *a, char *b) {
|
||||
fprint(stderr, msg, a, b);
|
||||
fprint(stderr, "(free[0]=%x)\n", freemask[0]);
|
||||
fprint(stderr, "(free[1]=%x)\n", freemask[1]);
|
||||
}
|
||||
|
||||
int getregnum(Node p) {
|
||||
assert(p && p->syms[RX] && p->syms[RX]->x.regnode);
|
||||
return p->syms[RX]->x.regnode->number;
|
||||
}
|
||||
|
||||
|
||||
unsigned regloc(Symbol p) {
|
||||
assert(p && p->sclass == REGISTER && p->sclass == REGISTER && p->x.regnode);
|
||||
return p->x.regnode->set<<8 | p->x.regnode->number;
|
||||
}
|
||||
317
src/cmd/lccom-1/init.c
Normal file
317
src/cmd/lccom-1/init.c
Normal file
@@ -0,0 +1,317 @@
|
||||
#include "c.h"
|
||||
|
||||
static int curseg; /* current segment */
|
||||
|
||||
/* defpointer - initialize a pointer to p or to 0 if p==0 */
|
||||
void defpointer(Symbol p) {
|
||||
if (p) {
|
||||
(*IR->defaddress)(p);
|
||||
p->ref++;
|
||||
} else {
|
||||
static Value v;
|
||||
(*IR->defconst)(P, voidptype->size, v);
|
||||
}
|
||||
}
|
||||
|
||||
/* genconst - generate/check constant expression e; return size */
|
||||
static int genconst(Tree e, int def) {
|
||||
for (;;)
|
||||
switch (generic(e->op)) {
|
||||
case ADDRG:
|
||||
if (def)
|
||||
(*IR->defaddress)(e->u.sym);
|
||||
return e->type->size;
|
||||
case CNST:
|
||||
if (e->op == CNST+P && isarray(e->type)) {
|
||||
e = cvtconst(e);
|
||||
continue;
|
||||
}
|
||||
if (def)
|
||||
(*IR->defconst)(e->type->op, e->type->size, e->u.v);
|
||||
return e->type->size;
|
||||
case RIGHT:
|
||||
assert(e->kids[0] || e->kids[1]);
|
||||
if (e->kids[1] && e->kids[0])
|
||||
error("initializer must be constant\n");
|
||||
e = e->kids[1] ? e->kids[1] : e->kids[0];
|
||||
continue;
|
||||
case CVP:
|
||||
if (isarith(e->type))
|
||||
error("cast from `%t' to `%t' is illegal in constant expressions\n",
|
||||
e->kids[0]->type, e->type);
|
||||
/* fall thru */
|
||||
case CVI: case CVU: case CVF:
|
||||
e = e->kids[0];
|
||||
continue;
|
||||
default:
|
||||
error("initializer must be constant\n");
|
||||
if (def)
|
||||
genconst(consttree(0, inttype), def);
|
||||
return inttype->size;
|
||||
}
|
||||
}
|
||||
|
||||
/* initvalue - evaluate a constant expression for a value of integer type ty */
|
||||
static Tree initvalue(Type ty) {
|
||||
Type aty;
|
||||
Tree e;
|
||||
|
||||
needconst++;
|
||||
e = expr1(0);
|
||||
if ((aty = assign(ty, e)) != NULL)
|
||||
e = cast(e, aty);
|
||||
else {
|
||||
error("invalid initialization type; found `%t' expected `%t'\n",
|
||||
e->type, ty);
|
||||
e = retype(consttree(0, inttype), ty);
|
||||
}
|
||||
needconst--;
|
||||
if (generic(e->op) != CNST) {
|
||||
error("initializer must be constant\n");
|
||||
e = retype(consttree(0, inttype), ty);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
/* initarray - initialize array of ty of <= len bytes; if len == 0, go to } */
|
||||
static int initarray(int len, Type ty, int lev) {
|
||||
int n = 0;
|
||||
|
||||
do {
|
||||
initializer(ty, lev);
|
||||
n += ty->size;
|
||||
if ((len > 0 && n >= len) || t != ',')
|
||||
break;
|
||||
t = gettok();
|
||||
} while (t != '}');
|
||||
return n;
|
||||
}
|
||||
|
||||
/* initchar - initialize array of <= len ty characters; if len == 0, go to } */
|
||||
static int initchar(int len, Type ty) {
|
||||
int n = 0;
|
||||
char buf[16], *s = buf;
|
||||
|
||||
do {
|
||||
*s++ = initvalue(ty)->u.v.i;
|
||||
if (++n%inttype->size == 0) {
|
||||
(*IR->defstring)(inttype->size, buf);
|
||||
s = buf;
|
||||
}
|
||||
if ((len > 0 && n >= len) || t != ',')
|
||||
break;
|
||||
t = gettok();
|
||||
} while (t != '}');
|
||||
if (s > buf)
|
||||
(*IR->defstring)(s - buf, buf);
|
||||
return n;
|
||||
}
|
||||
|
||||
/* initend - finish off an initialization at level lev; accepts trailing comma */
|
||||
static void initend(int lev, char follow[]) {
|
||||
if (lev == 0 && t == ',')
|
||||
t = gettok();
|
||||
test('}', follow);
|
||||
}
|
||||
|
||||
/* initfields - initialize <= an unsigned's worth of bit fields in fields p to q */
|
||||
static int initfields(Field p, Field q) {
|
||||
unsigned int bits = 0;
|
||||
int i, n = 0;
|
||||
|
||||
do {
|
||||
i = initvalue(inttype)->u.v.i;
|
||||
if (fieldsize(p) < 8*p->type->size) {
|
||||
if ((p->type == inttype &&
|
||||
(i < -(int)(fieldmask(p)>>1)-1 || i > (int)(fieldmask(p)>>1)))
|
||||
|| (p->type == unsignedtype && (i&~fieldmask(p)) != 0))
|
||||
warning("initializer exceeds bit-field width\n");
|
||||
i &= fieldmask(p);
|
||||
}
|
||||
bits |= i<<fieldright(p);
|
||||
if (IR->little_endian) {
|
||||
if (fieldsize(p) + fieldright(p) > n)
|
||||
n = fieldsize(p) + fieldright(p);
|
||||
} else {
|
||||
if (fieldsize(p) + fieldleft(p) > n)
|
||||
n = fieldsize(p) + fieldleft(p);
|
||||
}
|
||||
if (p->link == q)
|
||||
break;
|
||||
p = p->link;
|
||||
} while (t == ',' && (t = gettok()) != 0);
|
||||
n = (n + 7)/8;
|
||||
for (i = 0; i < n; i++) {
|
||||
Value v;
|
||||
if (IR->little_endian) {
|
||||
v.u = (unsigned char)bits;
|
||||
bits >>= 8;
|
||||
} else { /* a big endian */
|
||||
v.u = (unsigned char)(bits>>(8*(unsignedtype->size - 1)));
|
||||
bits <<= 8;
|
||||
}
|
||||
(*IR->defconst)(U, unsignedchar->size, v);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/* initstruct - initialize a struct ty of <= len bytes; if len == 0, go to } */
|
||||
static int initstruct(int len, Type ty, int lev) {
|
||||
int a, n = 0;
|
||||
Field p = ty->u.sym->u.s.flist;
|
||||
|
||||
do {
|
||||
if (p->offset > n) {
|
||||
(*IR->space)(p->offset - n);
|
||||
n += p->offset - n;
|
||||
}
|
||||
if (p->lsb) {
|
||||
Field q = p;
|
||||
while (q->link && q->link->offset == p->offset)
|
||||
q = q->link;
|
||||
n += initfields(p, q->link);
|
||||
p = q;
|
||||
} else {
|
||||
initializer(p->type, lev);
|
||||
n += p->type->size;
|
||||
}
|
||||
if (p->link) {
|
||||
p = p->link;
|
||||
a = p->type->align;
|
||||
} else
|
||||
a = ty->align;
|
||||
if (a && n%a) {
|
||||
(*IR->space)(a - n%a);
|
||||
n = roundup(n, a);
|
||||
}
|
||||
if ((len > 0 && n >= len) || t != ',')
|
||||
break;
|
||||
t = gettok();
|
||||
} while (t != '}');
|
||||
return n;
|
||||
}
|
||||
|
||||
/* initializer - constexpr | { constexpr ( , constexpr )* [ , ] } */
|
||||
Type initializer(Type ty, int lev) {
|
||||
int n = 0;
|
||||
Tree e;
|
||||
Type aty = NULL;
|
||||
static char follow[] = { IF, CHAR, STATIC, 0 };
|
||||
|
||||
ty = unqual(ty);
|
||||
if (isscalar(ty)) {
|
||||
needconst++;
|
||||
if (t == '{') {
|
||||
t = gettok();
|
||||
e = expr1(0);
|
||||
initend(lev, follow);
|
||||
} else
|
||||
e = expr1(0);
|
||||
e = pointer(e);
|
||||
if ((aty = assign(ty, e)) != NULL)
|
||||
e = cast(e, aty);
|
||||
else
|
||||
error("invalid initialization type; found `%t' expected `%t'\n",
|
||||
e->type, ty);
|
||||
n = genconst(e, 1);
|
||||
deallocate(STMT);
|
||||
needconst--;
|
||||
}
|
||||
if ((isunion(ty) || isstruct(ty)) && ty->size == 0) {
|
||||
static char follow[] = { CHAR, STATIC, 0 };
|
||||
error("cannot initialize undefined `%t'\n", ty);
|
||||
skipto(';', follow);
|
||||
return ty;
|
||||
} else if (isunion(ty)) {
|
||||
if (t == '{') {
|
||||
t = gettok();
|
||||
n = initstruct(ty->u.sym->u.s.flist->type->size, ty, lev + 1);
|
||||
initend(lev, follow);
|
||||
} else {
|
||||
if (lev == 0)
|
||||
error("missing { in initialization of `%t'\n", ty);
|
||||
n = initstruct(ty->u.sym->u.s.flist->type->size, ty, lev + 1);
|
||||
}
|
||||
} else if (isstruct(ty)) {
|
||||
if (t == '{') {
|
||||
t = gettok();
|
||||
n = initstruct(0, ty, lev + 1);
|
||||
test('}', follow);
|
||||
} else if (lev > 0)
|
||||
n = initstruct(ty->size, ty, lev + 1);
|
||||
else {
|
||||
error("missing { in initialization of `%t'\n", ty);
|
||||
n = initstruct(ty->u.sym->u.s.flist->type->size, ty, lev + 1);
|
||||
}
|
||||
}
|
||||
if (isarray(ty))
|
||||
aty = unqual(ty->type);
|
||||
if (isarray(ty) && ischar(aty)) {
|
||||
if (t == SCON) {
|
||||
if (ty->size > 0 && ty->size == tsym->type->size - 1)
|
||||
tsym->type = array(chartype, ty->size, 0);
|
||||
n = tsym->type->size;
|
||||
(*IR->defstring)(tsym->type->size, tsym->u.c.v.p);
|
||||
t = gettok();
|
||||
} else if (t == '{') {
|
||||
t = gettok();
|
||||
if (t == SCON) {
|
||||
ty = initializer(ty, lev + 1);
|
||||
initend(lev, follow);
|
||||
return ty;
|
||||
}
|
||||
n = initchar(0, aty);
|
||||
test('}', follow);
|
||||
} else if (lev > 0 && ty->size > 0)
|
||||
n = initchar(ty->size, aty);
|
||||
else { /* eg, char c[] = 0; */
|
||||
error("missing { in initialization of `%t'\n", ty);
|
||||
n = initchar(1, aty);
|
||||
}
|
||||
} else if (isarray(ty)) {
|
||||
if (t == SCON && aty == widechar) {
|
||||
int i;
|
||||
unsigned int *s = tsym->u.c.v.p;
|
||||
if (ty->size > 0 && ty->size == tsym->type->size - widechar->size)
|
||||
tsym->type = array(widechar, ty->size/widechar->size, 0);
|
||||
n = tsym->type->size;
|
||||
for (i = 0; i < n; i += widechar->size) {
|
||||
Value v;
|
||||
v.u = *s++;
|
||||
(*IR->defconst)(widechar->op, widechar->size, v);
|
||||
}
|
||||
t = gettok();
|
||||
} else if (t == '{') {
|
||||
t = gettok();
|
||||
if (t == SCON && aty == widechar) {
|
||||
ty = initializer(ty, lev + 1);
|
||||
initend(lev, follow);
|
||||
return ty;
|
||||
}
|
||||
n = initarray(0, aty, lev + 1);
|
||||
test('}', follow);
|
||||
} else if (lev > 0 && ty->size > 0)
|
||||
n = initarray(ty->size, aty, lev + 1);
|
||||
else {
|
||||
error("missing { in initialization of `%t'\n", ty);
|
||||
n = initarray(aty->size, aty, lev + 1);
|
||||
}
|
||||
}
|
||||
if (ty->size) {
|
||||
if (n > ty->size)
|
||||
error("too many initializers\n");
|
||||
else if (n < ty->size)
|
||||
(*IR->space)(ty->size - n);
|
||||
} else if (isarray(ty) && ty->type->size > 0)
|
||||
ty = array(ty->type, n/ty->type->size, 0);
|
||||
else
|
||||
ty->size = n;
|
||||
return ty;
|
||||
}
|
||||
|
||||
/* swtoseg - switch to segment seg, if necessary */
|
||||
void swtoseg(int seg) {
|
||||
if (curseg != seg)
|
||||
(*IR->segment)(seg);
|
||||
curseg = seg;
|
||||
}
|
||||
6
src/cmd/lccom-1/inits.c
Normal file
6
src/cmd/lccom-1/inits.c
Normal file
@@ -0,0 +1,6 @@
|
||||
void init(int argc, char *argv[])
|
||||
{
|
||||
{extern void input_init(int, char *[]); input_init(argc, argv);}
|
||||
{extern void main_init(int, char *[]); main_init(argc, argv);}
|
||||
{extern void type_init(int, char *[]); type_init(argc, argv);}
|
||||
}
|
||||
144
src/cmd/lccom-1/input.c
Normal file
144
src/cmd/lccom-1/input.c
Normal file
@@ -0,0 +1,144 @@
|
||||
#include "c.h"
|
||||
|
||||
static void pragma(void);
|
||||
static void resynch(void);
|
||||
|
||||
static int bsize;
|
||||
static unsigned char buffer[MAXLINE+1 + BUFSIZE+1];
|
||||
unsigned char *cp; /* current input character */
|
||||
char *file; /* current input file name */
|
||||
char *firstfile; /* first input file */
|
||||
unsigned char *limit; /* points to last character + 1 */
|
||||
char *line; /* current line */
|
||||
int lineno; /* line number of current line */
|
||||
|
||||
void nextline(void) {
|
||||
do {
|
||||
if (cp >= limit) {
|
||||
fillbuf();
|
||||
if (cp >= limit)
|
||||
cp = limit;
|
||||
if (cp == limit)
|
||||
return;
|
||||
} else {
|
||||
lineno++;
|
||||
for (line = (char *)cp; *cp==' ' || *cp=='\t'; cp++)
|
||||
;
|
||||
if (*cp == '#') {
|
||||
resynch();
|
||||
nextline();
|
||||
}
|
||||
}
|
||||
} while (*cp == '\n' && cp == limit);
|
||||
}
|
||||
void fillbuf(void) {
|
||||
if (bsize == 0)
|
||||
return;
|
||||
if (cp >= limit)
|
||||
cp = &buffer[MAXLINE+1];
|
||||
else
|
||||
{
|
||||
int n = limit - cp;
|
||||
unsigned char *s = &buffer[MAXLINE+1] - n;
|
||||
assert(s >= buffer);
|
||||
line = (char *)s - ((char *)cp - line);
|
||||
while (cp < limit)
|
||||
*s++ = *cp++;
|
||||
cp = &buffer[MAXLINE+1] - n;
|
||||
}
|
||||
if (feof(stdin))
|
||||
bsize = 0;
|
||||
else
|
||||
bsize = fread(&buffer[MAXLINE+1], 1, BUFSIZE, stdin);
|
||||
if (bsize < 0) {
|
||||
error("read error\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
limit = &buffer[MAXLINE+1+bsize];
|
||||
*limit = '\n';
|
||||
}
|
||||
void input_init(int argc, char *argv[]) {
|
||||
static int inited;
|
||||
|
||||
if (inited)
|
||||
return;
|
||||
inited = 1;
|
||||
main_init(argc, argv);
|
||||
limit = cp = &buffer[MAXLINE+1];
|
||||
bsize = -1;
|
||||
lineno = 0;
|
||||
file = NULL;
|
||||
fillbuf();
|
||||
if (cp >= limit)
|
||||
cp = limit;
|
||||
nextline();
|
||||
}
|
||||
|
||||
/* ident - handle #ident "string" */
|
||||
static void ident(void) {
|
||||
while (*cp != '\n' && *cp != '\0')
|
||||
cp++;
|
||||
}
|
||||
|
||||
/* pragma - handle #pragma ref id... */
|
||||
static void pragma(void) {
|
||||
if ((t = gettok()) == ID && strcmp(token, "ref") == 0)
|
||||
for (;;) {
|
||||
while (*cp == ' ' || *cp == '\t')
|
||||
cp++;
|
||||
if (*cp == '\n' || *cp == 0)
|
||||
break;
|
||||
if ((t = gettok()) == ID && tsym) {
|
||||
tsym->ref++;
|
||||
use(tsym, src);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* resynch - set line number/file name in # n [ "file" ], #pragma, etc. */
|
||||
static void resynch(void) {
|
||||
for (cp++; *cp == ' ' || *cp == '\t'; )
|
||||
cp++;
|
||||
if (limit - cp < MAXLINE)
|
||||
fillbuf();
|
||||
if (strncmp((char *)cp, "pragma", 6) == 0) {
|
||||
cp += 6;
|
||||
pragma();
|
||||
} else if (strncmp((char *)cp, "ident", 5) == 0) {
|
||||
cp += 5;
|
||||
ident();
|
||||
} else if (*cp >= '0' && *cp <= '9') {
|
||||
line: for (lineno = 0; *cp >= '0' && *cp <= '9'; )
|
||||
lineno = 10*lineno + *cp++ - '0';
|
||||
lineno--;
|
||||
while (*cp == ' ' || *cp == '\t')
|
||||
cp++;
|
||||
if (*cp == '"') {
|
||||
file = (char *)++cp;
|
||||
while (*cp && *cp != '"' && *cp != '\n')
|
||||
cp++;
|
||||
file = stringn(file, (char *)cp - file);
|
||||
if (*cp == '\n')
|
||||
warning("missing \" in preprocessor line\n");
|
||||
if (firstfile == 0)
|
||||
firstfile = file;
|
||||
}
|
||||
} else if (strncmp((char *)cp, "line", 4) == 0) {
|
||||
for (cp += 4; *cp == ' ' || *cp == '\t'; )
|
||||
cp++;
|
||||
if (*cp >= '0' && *cp <= '9')
|
||||
goto line;
|
||||
if (Aflag >= 2)
|
||||
warning("unrecognized control line\n");
|
||||
} else if (Aflag >= 2 && *cp != '\n')
|
||||
warning("unrecognized control line\n");
|
||||
while (*cp)
|
||||
if (*cp++ == '\n') {
|
||||
if (cp == limit + 1) {
|
||||
nextline();
|
||||
if (cp == limit)
|
||||
break;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
}
|
||||
673
src/cmd/lccom-1/lburg/gram.c
Normal file
673
src/cmd/lccom-1/lburg/gram.c
Normal file
@@ -0,0 +1,673 @@
|
||||
#if defined(__STDC__) || defined(__cplusplus)
|
||||
#define YYCONST const
|
||||
#define YYPARAMS(x) x
|
||||
#define YYDEFUN(name, arglist, args) name(args)
|
||||
#define YYAND ,
|
||||
#define YYPTR void *
|
||||
#else
|
||||
#define YYCONST
|
||||
#define YYPARAMS(x) ()
|
||||
#define YYDEFUN(name, arglist, args) name arglist args;
|
||||
#define YYAND ;
|
||||
#define YYPTR char *
|
||||
#endif
|
||||
#ifndef lint
|
||||
YYCONST static char yysccsid[] = "@(#)yaccpar 1.8 (Berkeley +Cygnus.28) 01/20/91";
|
||||
#endif
|
||||
#define YYBYACC 1
|
||||
#ifndef YYDONT_INCLUDE_STDIO
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
//#ifdef __cplusplus
|
||||
#include <stdlib.h> /* for malloc/realloc/free */
|
||||
//#endif
|
||||
#line 2 "lburg/gram.y"
|
||||
#include <stdio.h>
|
||||
#include "lburg.h"
|
||||
/*lint -e616 -e527 -e652 -esym(552,yynerrs) -esym(563,yynewstate,yyerrlab) */
|
||||
static int yylineno = 0;
|
||||
#line 8 "lburg/gram.y"
|
||||
typedef union {
|
||||
int n;
|
||||
char *string;
|
||||
Tree tree;
|
||||
} YYSTYPE;
|
||||
#line 37 "y.tab.c"
|
||||
#define TERMINAL 257
|
||||
#define START 258
|
||||
#define PPERCENT 259
|
||||
#define ID 260
|
||||
#define TEMPLATE 261
|
||||
#define CODE 262
|
||||
#define INT 263
|
||||
#define YYERRCODE 256
|
||||
static YYCONST short yylhs[] = { -1,
|
||||
0, 0, 4, 4, 6, 6, 6, 6, 7, 7,
|
||||
5, 5, 5, 5, 1, 3, 3, 3, 2,
|
||||
};
|
||||
static YYCONST short yylen[] = { 2,
|
||||
3, 1, 0, 2, 3, 3, 1, 2, 0, 4,
|
||||
0, 7, 2, 3, 1, 1, 4, 6, 1,
|
||||
};
|
||||
static YYCONST short yydefred[] = { 3,
|
||||
0, 0, 0, 9, 0, 11, 7, 4, 8, 0,
|
||||
15, 0, 0, 0, 5, 6, 0, 13, 0, 0,
|
||||
14, 0, 10, 0, 0, 0, 0, 0, 19, 0,
|
||||
17, 0, 12, 0, 18,
|
||||
};
|
||||
static YYCONST short yydgoto[] = { 1,
|
||||
12, 30, 25, 2, 13, 8, 10,
|
||||
};
|
||||
static YYCONST short yysindex[] = { 0,
|
||||
0, -4, -2, 0, -250, 0, 0, 0, 0, -9,
|
||||
0, 1, -10, -49, 0, 0, 3, 0, -44, -248,
|
||||
0, -244, 0, -22, -242, -244, -245, -37, 0, 10,
|
||||
0, -244, 0, -20, 0,
|
||||
};
|
||||
static YYCONST short yyrindex[] = { 0,
|
||||
0, 22, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 23, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, -39, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0,
|
||||
};
|
||||
static YYCONST short yygindex[] = { 0,
|
||||
11, 0, -23, 0, 0, 0, 0,
|
||||
};
|
||||
#define YYTABLESIZE 255
|
||||
static YYCONST short yytable[] = { 18,
|
||||
15, 16, 28, 31, 16, 7, 32, 9, 34, 11,
|
||||
16, 20, 21, 22, 23, 24, 29, 26, 27, 33,
|
||||
35, 2, 1, 19, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 16, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 17, 0, 0, 0, 11,
|
||||
14, 3, 4, 5, 6,
|
||||
};
|
||||
static YYCONST short yycheck[] = { 10,
|
||||
10, 41, 26, 41, 44, 10, 44, 10, 32, 260,
|
||||
10, 61, 10, 58, 263, 260, 262, 40, 261, 10,
|
||||
41, 0, 0, 13, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, 261, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, 256, -1, -1, -1, 260,
|
||||
260, 256, 257, 258, 259,
|
||||
};
|
||||
#define YYFINAL 1
|
||||
#ifndef YYDEBUG
|
||||
#define YYDEBUG 0
|
||||
#endif
|
||||
#define YYMAXTOKEN 263
|
||||
#if YYDEBUG
|
||||
static YYCONST char *YYCONST yyname[] = {
|
||||
"end-of-file",0,0,0,0,0,0,0,0,0,"'\\n'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,"'('","')'",0,0,"','",0,0,0,0,0,0,0,0,0,0,0,0,0,"':'",0,0,
|
||||
"'='",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
"TERMINAL","START","PPERCENT","ID","TEMPLATE","CODE","INT",
|
||||
};
|
||||
static YYCONST char *YYCONST yyrule[] = {
|
||||
"$accept : spec",
|
||||
"spec : decls PPERCENT rules",
|
||||
"spec : decls",
|
||||
"decls :",
|
||||
"decls : decls decl",
|
||||
"decl : TERMINAL blist '\\n'",
|
||||
"decl : START nonterm '\\n'",
|
||||
"decl : '\\n'",
|
||||
"decl : error '\\n'",
|
||||
"blist :",
|
||||
"blist : blist ID '=' INT",
|
||||
"rules :",
|
||||
"rules : rules nonterm ':' tree TEMPLATE cost '\\n'",
|
||||
"rules : rules '\\n'",
|
||||
"rules : rules error '\\n'",
|
||||
"nonterm : ID",
|
||||
"tree : ID",
|
||||
"tree : ID '(' tree ')'",
|
||||
"tree : ID '(' tree ',' tree ')'",
|
||||
"cost : CODE",
|
||||
};
|
||||
#endif
|
||||
#define YYLEX yylex()
|
||||
#define YYEMPTY -1
|
||||
#define yyclearin (yychar=(YYEMPTY))
|
||||
#define yyerrok (yyerrflag=0)
|
||||
#ifndef YYINITDEPTH
|
||||
#define YYINITDEPTH 200
|
||||
#endif
|
||||
#ifdef YYSTACKSIZE
|
||||
#ifndef YYMAXDEPTH
|
||||
#define YYMAXDEPTH YYSTACKSIZE
|
||||
#endif
|
||||
#else
|
||||
#ifdef YYMAXDEPTH
|
||||
#define YYSTACKSIZE YYMAXDEPTH
|
||||
#else
|
||||
#define YYSTACKSIZE 500
|
||||
#define YYMAXDEPTH 500
|
||||
#endif
|
||||
#endif
|
||||
#ifndef YYMAXSTACKSIZE
|
||||
#define YYMAXSTACKSIZE 10000
|
||||
#endif
|
||||
int yydebug;
|
||||
int yynerrs;
|
||||
int yyerrflag;
|
||||
int yychar;
|
||||
YYSTYPE yyval;
|
||||
YYSTYPE yylval;
|
||||
static short *yyss;
|
||||
static YYSTYPE *yyvs;
|
||||
static int yystacksize;
|
||||
#define yyfree(x) free(x)
|
||||
extern int yylex();
|
||||
|
||||
static YYPTR
|
||||
YYDEFUN (yymalloc, (bytes), unsigned bytes)
|
||||
{
|
||||
YYPTR ptr = (YYPTR) malloc (bytes);
|
||||
if (ptr != 0) return (ptr);
|
||||
yyerror ("yyparse: memory exhausted");
|
||||
return (0);
|
||||
}
|
||||
|
||||
static YYPTR
|
||||
YYDEFUN (yyrealloc, (old, bytes), YYPTR old YYAND unsigned bytes)
|
||||
{
|
||||
YYPTR ptr = (YYPTR) realloc (old, bytes);
|
||||
if (ptr != 0) return (ptr);
|
||||
yyerror ("yyparse: memory exhausted");
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
#ifdef __GNUC__
|
||||
inline
|
||||
#endif
|
||||
yygrow ()
|
||||
{
|
||||
#if YYDEBUG
|
||||
int old_stacksize = yystacksize;
|
||||
#endif
|
||||
short *new_yyss;
|
||||
YYSTYPE *new_yyvs;
|
||||
|
||||
if (yystacksize == YYMAXSTACKSIZE)
|
||||
return (1);
|
||||
yystacksize += (yystacksize + 1 ) / 2;
|
||||
if (yystacksize > YYMAXSTACKSIZE)
|
||||
yystacksize = YYMAXSTACKSIZE;
|
||||
#if YYDEBUG
|
||||
if (yydebug)
|
||||
printf("yydebug: growing stack size from %d to %d\n",
|
||||
old_stacksize, yystacksize);
|
||||
#endif
|
||||
new_yyss = (short *) yyrealloc ((char *)yyss, yystacksize * sizeof (short));
|
||||
if (new_yyss == 0)
|
||||
return (1);
|
||||
new_yyvs = (YYSTYPE *) yyrealloc ((char *)yyvs, yystacksize * sizeof (YYSTYPE));
|
||||
if (new_yyvs == 0)
|
||||
{
|
||||
yyfree (new_yyss);
|
||||
return (1);
|
||||
}
|
||||
yyss = new_yyss;
|
||||
yyvs = new_yyvs;
|
||||
return (0);
|
||||
}
|
||||
#line 60 "lburg/gram.y"
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
int errcnt = 0;
|
||||
FILE *infp = NULL;
|
||||
FILE *outfp = NULL;
|
||||
static char buf[BUFSIZ], *bp = buf;
|
||||
static int ppercent = 0;
|
||||
static int code = 0;
|
||||
|
||||
static int get(void) {
|
||||
if (*bp == 0) {
|
||||
bp = buf;
|
||||
*bp = 0;
|
||||
if (fgets(buf, sizeof buf, infp) == NULL)
|
||||
return EOF;
|
||||
yylineno++;
|
||||
while (buf[0] == '%' && buf[1] == '{' && buf[2] == '\n') {
|
||||
for (;;) {
|
||||
if (fgets(buf, sizeof buf, infp) == NULL) {
|
||||
yywarn("unterminated %{...%}\n");
|
||||
return EOF;
|
||||
}
|
||||
yylineno++;
|
||||
if (strcmp(buf, "%}\n") == 0)
|
||||
break;
|
||||
fputs(buf, outfp);
|
||||
}
|
||||
if (fgets(buf, sizeof buf, infp) == NULL)
|
||||
return EOF;
|
||||
yylineno++;
|
||||
}
|
||||
}
|
||||
return *bp++;
|
||||
}
|
||||
|
||||
void yyerror(char *fmt, ...) {
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
if (yylineno > 0)
|
||||
fprintf(stderr, "line %d: ", yylineno);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
if (fmt[strlen(fmt)-1] != '\n')
|
||||
fprintf(stderr, "\n");
|
||||
errcnt++;
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
int yylex(void) {
|
||||
int c;
|
||||
|
||||
if (code) {
|
||||
char *p;
|
||||
bp += strspn(bp, " \t\f");
|
||||
p = strchr(bp, '\n');
|
||||
if (p == NULL)
|
||||
p = strchr(bp, '\n');
|
||||
while (p > bp && isspace(p[-1]))
|
||||
p--;
|
||||
yylval.string = alloc(p - bp + 1);
|
||||
strncpy(yylval.string, bp, p - bp);
|
||||
yylval.string[p - bp] = 0;
|
||||
bp = p;
|
||||
code--;
|
||||
return CODE;
|
||||
}
|
||||
while ((c = get()) != EOF) {
|
||||
switch (c) {
|
||||
case ' ': case '\f': case '\t':
|
||||
continue;
|
||||
case '\n':
|
||||
case '(': case ')': case ',':
|
||||
case ':': case '=':
|
||||
return c;
|
||||
}
|
||||
if (c == '%' && *bp == '%') {
|
||||
bp++;
|
||||
return ppercent++ ? 0 : PPERCENT;
|
||||
} else if (c == '%' && strncmp(bp, "term", 4) == 0
|
||||
&& isspace(bp[4])) {
|
||||
bp += 4;
|
||||
return TERMINAL;
|
||||
} else if (c == '%' && strncmp(bp, "start", 5) == 0
|
||||
&& isspace(bp[5])) {
|
||||
bp += 5;
|
||||
return START;
|
||||
} else if (c == '"') {
|
||||
char *p = strchr(bp, '"');
|
||||
if (p == NULL) {
|
||||
yyerror("missing \" in assembler template\n");
|
||||
p = strchr(bp, '\n');
|
||||
if (p == NULL)
|
||||
p = strchr(bp, '\0');
|
||||
}
|
||||
assert(p);
|
||||
yylval.string = alloc(p - bp + 1);
|
||||
strncpy(yylval.string, bp, p - bp);
|
||||
yylval.string[p - bp] = 0;
|
||||
bp = *p == '"' ? p + 1 : p;
|
||||
code++;
|
||||
return TEMPLATE;
|
||||
} else if (isdigit(c)) {
|
||||
int n = 0;
|
||||
do {
|
||||
int d = c - '0';
|
||||
if (n > (INT_MAX - d)/10)
|
||||
yyerror("integer greater than %d\n", INT_MAX);
|
||||
else
|
||||
n = 10*n + d;
|
||||
c = get();
|
||||
} while (c != EOF && isdigit(c));
|
||||
bp--;
|
||||
yylval.n = n;
|
||||
return INT;
|
||||
} else if (isalpha(c)) {
|
||||
char *p = bp - 1;
|
||||
while (isalpha(*bp) || isdigit(*bp) || *bp == '_')
|
||||
bp++;
|
||||
yylval.string = alloc(bp - p + 1);
|
||||
strncpy(yylval.string, p, bp - p);
|
||||
yylval.string[bp - p] = 0;
|
||||
return ID;
|
||||
} else if (isprint(c))
|
||||
yyerror("invalid character `%c'\n", c);
|
||||
else
|
||||
yyerror("invalid character `\\%03o'\n", (unsigned char)c);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void yywarn(char *fmt, ...) {
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
if (yylineno > 0)
|
||||
fprintf(stderr, "line %d: ", yylineno);
|
||||
fprintf(stderr, "warning: ");
|
||||
vfprintf(stderr, fmt, ap);
|
||||
}
|
||||
#line 403 "y.tab.c"
|
||||
#define YYABORT goto yyabort
|
||||
#define YYACCEPT goto yyaccept
|
||||
|
||||
#if YYDEBUG
|
||||
#ifdef __cplusplus
|
||||
extern "C" char *getenv();
|
||||
#else
|
||||
extern char *getenv();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
int
|
||||
yyparse()
|
||||
{
|
||||
register int yym, yyn, yystate;
|
||||
register YYSTYPE *yyvsp;
|
||||
register short *yyssp;
|
||||
short *yysse;
|
||||
#if YYDEBUG
|
||||
register YYCONST char *yys;
|
||||
|
||||
if (yys = getenv("YYDEBUG"))
|
||||
{
|
||||
yyn = *yys;
|
||||
if (yyn >= '0' && yyn <= '9')
|
||||
yydebug = yyn - '0';
|
||||
}
|
||||
#endif
|
||||
|
||||
yynerrs = 0;
|
||||
yyerrflag = 0;
|
||||
yychar = (-1);
|
||||
|
||||
if (yyss == 0)
|
||||
{
|
||||
yyss = (short *) yymalloc (YYSTACKSIZE * sizeof (short));
|
||||
if (yyss == 0)
|
||||
goto yyabort;
|
||||
yyvs = (YYSTYPE *) yymalloc (YYSTACKSIZE * sizeof (YYSTYPE));
|
||||
if (yyvs == 0)
|
||||
{
|
||||
yyfree (yyss);
|
||||
goto yyabort;
|
||||
}
|
||||
yystacksize = YYSTACKSIZE;
|
||||
}
|
||||
yysse = yyss + yystacksize - 1;
|
||||
yyssp = yyss;
|
||||
yyvsp = yyvs;
|
||||
*yyssp = yystate = 0;
|
||||
goto yyloop;
|
||||
|
||||
yypush_lex:
|
||||
yyval = yylval;
|
||||
yystate = yytable[yyn];
|
||||
yypush:
|
||||
if (yyssp >= yysse)
|
||||
{
|
||||
int depth = yyssp - yyss;
|
||||
if (yygrow() != 0)
|
||||
goto yyoverflow;
|
||||
yysse = yyss + yystacksize -1;
|
||||
yyssp = depth + yyss;
|
||||
yyvsp = depth + yyvs;
|
||||
}
|
||||
*++yyssp = yystate;
|
||||
*++yyvsp = yyval;
|
||||
|
||||
yyloop:
|
||||
if ((yyn = yydefred[yystate])) goto yyreduce;
|
||||
yyn = yysindex[yystate];
|
||||
if (yychar < 0)
|
||||
{
|
||||
if ((yychar = yylex()) < 0) yychar = 0;
|
||||
#if YYDEBUG
|
||||
if (yydebug)
|
||||
{
|
||||
yys = 0;
|
||||
if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
|
||||
if (!yys) yys = "illegal-symbol";
|
||||
printf("yydebug: state %d, reading %d (%s)\n", yystate,
|
||||
yychar, yys);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (yyn != 0
|
||||
&& ((yyn += yychar), ((unsigned)yyn <= (unsigned)YYTABLESIZE))
|
||||
&& yycheck[yyn] == yychar)
|
||||
{
|
||||
#if YYDEBUG
|
||||
if (yydebug)
|
||||
printf("yydebug: state %d, shifting to state %d\n",
|
||||
yystate, yytable[yyn]);
|
||||
#endif
|
||||
if (yyerrflag > 0) --yyerrflag;
|
||||
yychar = (-1);
|
||||
goto yypush_lex;
|
||||
}
|
||||
yyn = yyrindex[yystate];
|
||||
if (yyn != 0
|
||||
&& ((yyn += yychar), ((unsigned)yyn <= (unsigned)YYTABLESIZE))
|
||||
&& yycheck[yyn] == yychar)
|
||||
{
|
||||
yyn = yytable[yyn];
|
||||
goto yyreduce;
|
||||
}
|
||||
if (yyerrflag) goto yyinrecovery;
|
||||
yyerror("syntax error");
|
||||
++yynerrs;
|
||||
yyinrecovery:
|
||||
if (yyerrflag < 3)
|
||||
{
|
||||
yyerrflag = 3;
|
||||
for (;;)
|
||||
{
|
||||
yyn = yysindex[*yyssp];
|
||||
if (yyn != 0
|
||||
&& ((yyn += YYERRCODE), ((unsigned)yyn <= (unsigned)YYTABLESIZE))
|
||||
&& yycheck[yyn] == YYERRCODE)
|
||||
{
|
||||
#if YYDEBUG
|
||||
if (yydebug)
|
||||
printf("yydebug: state %d, error recovery shifting\
|
||||
to state %d\n", *yyssp, yytable[yyn]);
|
||||
#endif
|
||||
goto yypush_lex;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if YYDEBUG
|
||||
if (yydebug)
|
||||
printf("yydebug: error recovery discarding state %d\n",
|
||||
*yyssp);
|
||||
#endif
|
||||
if (yyssp <= yyss) goto yyabort;
|
||||
--yyssp;
|
||||
--yyvsp;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (yychar == 0) goto yyabort;
|
||||
#if YYDEBUG
|
||||
if (yydebug)
|
||||
{
|
||||
yys = 0;
|
||||
if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
|
||||
if (!yys) yys = "illegal-symbol";
|
||||
printf("yydebug: state %d, error recovery discards token %d (%s)\n",
|
||||
yystate, yychar, yys);
|
||||
}
|
||||
#endif
|
||||
yychar = (-1);
|
||||
goto yyloop;
|
||||
}
|
||||
yyreduce:
|
||||
#if YYDEBUG
|
||||
if (yydebug)
|
||||
printf("yydebug: state %d, reducing by rule %d (%s)\n",
|
||||
yystate, yyn, yyrule[yyn]);
|
||||
#endif
|
||||
yym = yylen[yyn];
|
||||
yyval = yyvsp[1-yym];
|
||||
switch (yyn)
|
||||
{
|
||||
case 1:
|
||||
#line 22 "lburg/gram.y"
|
||||
{ yylineno = 0; }
|
||||
break;
|
||||
case 2:
|
||||
#line 23 "lburg/gram.y"
|
||||
{ yylineno = 0; }
|
||||
break;
|
||||
case 6:
|
||||
#line 31 "lburg/gram.y"
|
||||
{
|
||||
if (nonterm(yyvsp[-1].string)->number != 1)
|
||||
yyerror("redeclaration of the start symbol\n");
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
#line 36 "lburg/gram.y"
|
||||
{ yyerrok; }
|
||||
break;
|
||||
case 10:
|
||||
#line 40 "lburg/gram.y"
|
||||
{ term(yyvsp[-2].string, yyvsp[0].n); }
|
||||
break;
|
||||
case 12:
|
||||
#line 44 "lburg/gram.y"
|
||||
{ rule(yyvsp[-5].string, yyvsp[-3].tree, yyvsp[-2].string, yyvsp[-1].string); }
|
||||
break;
|
||||
case 14:
|
||||
#line 46 "lburg/gram.y"
|
||||
{ yyerrok; }
|
||||
break;
|
||||
case 15:
|
||||
#line 49 "lburg/gram.y"
|
||||
{ nonterm(yyval.string = yyvsp[0].string); }
|
||||
break;
|
||||
case 16:
|
||||
#line 52 "lburg/gram.y"
|
||||
{ yyval.tree = tree(yyvsp[0].string, 0, 0); }
|
||||
break;
|
||||
case 17:
|
||||
#line 53 "lburg/gram.y"
|
||||
{ yyval.tree = tree(yyvsp[-3].string, yyvsp[-1].tree, 0); }
|
||||
break;
|
||||
case 18:
|
||||
#line 54 "lburg/gram.y"
|
||||
{ yyval.tree = tree(yyvsp[-5].string, yyvsp[-3].tree, yyvsp[-1].tree); }
|
||||
break;
|
||||
case 19:
|
||||
#line 57 "lburg/gram.y"
|
||||
{ if (*yyvsp[0].string == 0) yyval.string = "0"; }
|
||||
break;
|
||||
#line 630 "y.tab.c"
|
||||
}
|
||||
yyssp -= yym;
|
||||
yyvsp -= yym;
|
||||
yym = yylhs[yyn];
|
||||
yystate = *yyssp;
|
||||
if (yystate == 0 && yym == 0)
|
||||
{
|
||||
#if YYDEBUG
|
||||
if (yydebug)
|
||||
printf("yydebug: after reduction, shifting from state 0 to\
|
||||
state %d\n", YYFINAL);
|
||||
#endif
|
||||
yystate = YYFINAL;
|
||||
*++yyssp = YYFINAL;
|
||||
*++yyvsp = yyval;
|
||||
if (yychar < 0)
|
||||
{
|
||||
if ((yychar = yylex()) < 0) yychar = 0;
|
||||
#if YYDEBUG
|
||||
if (yydebug)
|
||||
{
|
||||
yys = 0;
|
||||
if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
|
||||
if (!yys) yys = "illegal-symbol";
|
||||
printf("yydebug: state %d, reading %d (%s)\n",
|
||||
YYFINAL, yychar, yys);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (yychar == 0) goto yyaccept;
|
||||
goto yyloop;
|
||||
}
|
||||
yyn = yygindex[yym];
|
||||
if (yyn != 0
|
||||
&& ((yyn += yystate), ((unsigned)yyn <= (unsigned)YYTABLESIZE))
|
||||
&& yycheck[yyn] == yystate)
|
||||
yystate = yytable[yyn];
|
||||
else
|
||||
yystate = yydgoto[yym];
|
||||
#if YYDEBUG
|
||||
if (yydebug)
|
||||
printf("yydebug: after reduction, shifting from state %d \
|
||||
to state %d\n", *yyssp, yystate);
|
||||
#endif
|
||||
goto yypush;
|
||||
yyoverflow:
|
||||
yyerror("yacc stack overflow");
|
||||
yyabort:
|
||||
return (1);
|
||||
yyaccept:
|
||||
return (0);
|
||||
}
|
||||
202
src/cmd/lccom-1/lburg/gram.y
Normal file
202
src/cmd/lccom-1/lburg/gram.y
Normal file
@@ -0,0 +1,202 @@
|
||||
%{
|
||||
#include <stdio.h>
|
||||
#include "lburg.h"
|
||||
static char rcsid[] = "$Id: gram.y,v 2.5 1997/11/21 18:59:34 drh Exp $";
|
||||
/*lint -e616 -e527 -e652 -esym(552,yynerrs) -esym(563,yynewstate,yyerrlab) */
|
||||
static int yylineno = 0;
|
||||
%}
|
||||
%union {
|
||||
int n;
|
||||
char *string;
|
||||
Tree tree;
|
||||
}
|
||||
%term TERMINAL
|
||||
%term START
|
||||
%term PPERCENT
|
||||
|
||||
%token <string> ID TEMPLATE CODE
|
||||
%token <n> INT
|
||||
%type <string> nonterm cost
|
||||
%type <tree> tree
|
||||
%%
|
||||
spec : decls PPERCENT rules { yylineno = 0; }
|
||||
| decls { yylineno = 0; }
|
||||
;
|
||||
|
||||
decls : /* lambda */
|
||||
| decls decl
|
||||
;
|
||||
|
||||
decl : TERMINAL blist '\n'
|
||||
| START nonterm '\n' {
|
||||
if (nonterm($2)->number != 1)
|
||||
yyerror("redeclaration of the start symbol\n");
|
||||
}
|
||||
| '\n'
|
||||
| error '\n' { yyerrok; }
|
||||
;
|
||||
|
||||
blist : /* lambda */
|
||||
| blist ID '=' INT { term($2, $4); }
|
||||
;
|
||||
|
||||
rules : /* lambda */
|
||||
| rules nonterm ':' tree TEMPLATE cost '\n' { rule($2, $4, $5, $6); }
|
||||
| rules '\n'
|
||||
| rules error '\n' { yyerrok; }
|
||||
;
|
||||
|
||||
nonterm : ID { nonterm($$ = $1); }
|
||||
;
|
||||
|
||||
tree : ID { $$ = tree($1, 0, 0); }
|
||||
| ID '(' tree ')' { $$ = tree($1, $3, 0); }
|
||||
| ID '(' tree ',' tree ')' { $$ = tree($1, $3, $5); }
|
||||
;
|
||||
|
||||
cost : CODE { if (*$1 == 0) $$ = "0"; }
|
||||
;
|
||||
%%
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
int errcnt = 0;
|
||||
FILE *infp = NULL;
|
||||
FILE *outfp = NULL;
|
||||
static char buf[BUFSIZ], *bp = buf;
|
||||
static int ppercent = 0;
|
||||
static int code = 0;
|
||||
|
||||
static int get(void) {
|
||||
if (*bp == 0) {
|
||||
bp = buf;
|
||||
*bp = 0;
|
||||
if (fgets(buf, sizeof buf, infp) == NULL)
|
||||
return EOF;
|
||||
yylineno++;
|
||||
while (buf[0] == '%' && buf[1] == '{' && buf[2] == '\n') {
|
||||
for (;;) {
|
||||
if (fgets(buf, sizeof buf, infp) == NULL) {
|
||||
yywarn("unterminated %{...%}\n");
|
||||
return EOF;
|
||||
}
|
||||
yylineno++;
|
||||
if (strcmp(buf, "%}\n") == 0)
|
||||
break;
|
||||
fputs(buf, outfp);
|
||||
}
|
||||
if (fgets(buf, sizeof buf, infp) == NULL)
|
||||
return EOF;
|
||||
yylineno++;
|
||||
}
|
||||
}
|
||||
return *bp++;
|
||||
}
|
||||
|
||||
void yyerror(char *fmt, ...) {
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
if (yylineno > 0)
|
||||
fprintf(stderr, "line %d: ", yylineno);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
if (fmt[strlen(fmt)-1] != '\n')
|
||||
fprintf(stderr, "\n");
|
||||
errcnt++;
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
int yylex(void) {
|
||||
int c;
|
||||
|
||||
if (code) {
|
||||
char *p;
|
||||
bp += strspn(bp, " \t\f");
|
||||
p = strchr(bp, '\n');
|
||||
if (p == NULL)
|
||||
p = strchr(bp, '\n');
|
||||
while (p > bp && isspace(p[-1]))
|
||||
p--;
|
||||
yylval.string = alloc(p - bp + 1);
|
||||
strncpy(yylval.string, bp, p - bp);
|
||||
yylval.string[p - bp] = 0;
|
||||
bp = p;
|
||||
code--;
|
||||
return CODE;
|
||||
}
|
||||
while ((c = get()) != EOF) {
|
||||
switch (c) {
|
||||
case ' ': case '\f': case '\t':
|
||||
continue;
|
||||
case '\n':
|
||||
case '(': case ')': case ',':
|
||||
case ':': case '=':
|
||||
return c;
|
||||
}
|
||||
if (c == '%' && *bp == '%') {
|
||||
bp++;
|
||||
return ppercent++ ? 0 : PPERCENT;
|
||||
} else if (c == '%' && strncmp(bp, "term", 4) == 0
|
||||
&& isspace(bp[4])) {
|
||||
bp += 4;
|
||||
return TERMINAL;
|
||||
} else if (c == '%' && strncmp(bp, "start", 5) == 0
|
||||
&& isspace(bp[5])) {
|
||||
bp += 5;
|
||||
return START;
|
||||
} else if (c == '"') {
|
||||
char *p = strchr(bp, '"');
|
||||
if (p == NULL) {
|
||||
yyerror("missing \" in assembler template\n");
|
||||
p = strchr(bp, '\n');
|
||||
if (p == NULL)
|
||||
p = strchr(bp, '\0');
|
||||
}
|
||||
assert(p);
|
||||
yylval.string = alloc(p - bp + 1);
|
||||
strncpy(yylval.string, bp, p - bp);
|
||||
yylval.string[p - bp] = 0;
|
||||
bp = *p == '"' ? p + 1 : p;
|
||||
code++;
|
||||
return TEMPLATE;
|
||||
} else if (isdigit(c)) {
|
||||
int n = 0;
|
||||
do {
|
||||
int d = c - '0';
|
||||
if (n > (INT_MAX - d)/10)
|
||||
yyerror("integer greater than %d\n", INT_MAX);
|
||||
else
|
||||
n = 10*n + d;
|
||||
c = get();
|
||||
} while (c != EOF && isdigit(c));
|
||||
bp--;
|
||||
yylval.n = n;
|
||||
return INT;
|
||||
} else if (isalpha(c)) {
|
||||
char *p = bp - 1;
|
||||
while (isalpha(*bp) || isdigit(*bp) || *bp == '_')
|
||||
bp++;
|
||||
yylval.string = alloc(bp - p + 1);
|
||||
strncpy(yylval.string, p, bp - p);
|
||||
yylval.string[bp - p] = 0;
|
||||
return ID;
|
||||
} else if (isprint(c))
|
||||
yyerror("invalid character `%c'\n", c);
|
||||
else
|
||||
yyerror("invalid character `\\%03o'\n", (unsigned char)c);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void yywarn(char *fmt, ...) {
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
if (yylineno > 0)
|
||||
fprintf(stderr, "line %d: ", yylineno);
|
||||
fprintf(stderr, "warning: ");
|
||||
vfprintf(stderr, fmt, ap);
|
||||
}
|
||||
358
src/cmd/lccom-1/lburg/lburg.1
Normal file
358
src/cmd/lccom-1/lburg/lburg.1
Normal file
@@ -0,0 +1,358 @@
|
||||
.TH LBURG 1 "local \- 11/30/94"
|
||||
|
||||
.\" $Id: lburg.1,v 2.1 1994/11/30 21:53:18 drh Exp $
|
||||
|
||||
.SH NAME
|
||||
|
||||
lburg \- lcc's code-generator generator
|
||||
|
||||
.SH SYNOPSIS
|
||||
|
||||
.B lburg
|
||||
|
||||
[
|
||||
|
||||
.I option
|
||||
|
||||
]...
|
||||
|
||||
[ [
|
||||
|
||||
.I input
|
||||
|
||||
]
|
||||
|
||||
.I output
|
||||
|
||||
]
|
||||
|
||||
.br
|
||||
|
||||
.SH DESCRIPTION
|
||||
|
||||
.PP
|
||||
|
||||
.I lburg
|
||||
|
||||
reads an lcc-style BURG specification from
|
||||
|
||||
.I input
|
||||
|
||||
and writes a pattern-matching code generator to
|
||||
|
||||
.IR output .
|
||||
|
||||
If
|
||||
|
||||
.I input
|
||||
|
||||
is `\-' or is omitted,
|
||||
|
||||
.I lburg
|
||||
|
||||
reads the standard input;
|
||||
|
||||
If
|
||||
|
||||
.I output
|
||||
|
||||
is `\-' or is omitted,
|
||||
|
||||
.I lburg
|
||||
|
||||
writes to the standard output.
|
||||
|
||||
.PP
|
||||
|
||||
.I lburg
|
||||
|
||||
accepts specifications that conform to the following EBNF grammar.
|
||||
|
||||
Terminals are enclosed in single quotes or are
|
||||
|
||||
given in uppercase, all other symbols are nonterminals or English phrases,
|
||||
|
||||
{X} denotes zero or more instances of X, and [X] denotes an optional X.
|
||||
|
||||
.PP
|
||||
|
||||
.nf
|
||||
|
||||
.RS
|
||||
|
||||
.ft CW
|
||||
|
||||
spec: `%{' configuration `%}' { dcl } `%%' { rule }
|
||||
|
||||
[ `%%' C code ]
|
||||
|
||||
|
||||
|
||||
dcl: `%start' nonterm
|
||||
|
||||
`%term' { ID `=' INT }
|
||||
|
||||
|
||||
|
||||
rule: nonterm `:' tree template [ C expression ]
|
||||
|
||||
|
||||
|
||||
tree: term `(' tree `,' tree `)'
|
||||
|
||||
term `(' tree `)'
|
||||
|
||||
term
|
||||
|
||||
nonterm
|
||||
|
||||
|
||||
|
||||
nonterm: ID
|
||||
|
||||
|
||||
|
||||
template: `"' { any character except double quote } `"'
|
||||
|
||||
.RE
|
||||
|
||||
.fi
|
||||
|
||||
.PP
|
||||
|
||||
Specifications are structurally similar to
|
||||
|
||||
.IR yacc 's.
|
||||
|
||||
Text between
|
||||
|
||||
`\f(CW%{\fP'
|
||||
|
||||
and
|
||||
|
||||
`\f(CW%}\fP'
|
||||
|
||||
is called the configuration section; there may be several such segments.
|
||||
|
||||
All are concatenated and copied verbatim into the head of the output.
|
||||
|
||||
Text after the second
|
||||
|
||||
`\f(CW%%\fP',
|
||||
|
||||
if any, is also copied verbatim into the output, at the end.
|
||||
|
||||
.PP
|
||||
|
||||
Specifications consist of declarations, a
|
||||
|
||||
`\f(CW%%\fP'
|
||||
|
||||
separator, and rules.
|
||||
|
||||
Input is line-oriented; each declaration and rule must appear on a separate line,
|
||||
|
||||
and declarations must begin in column 1.
|
||||
|
||||
Declarations declare terminals \(em the operators in subject
|
||||
|
||||
trees \(em and associate a unique, positive external symbol
|
||||
|
||||
number with each one.
|
||||
|
||||
Nonterminals are declared by their presence
|
||||
|
||||
on the left side of rules. The
|
||||
|
||||
\f(CW%start\fP
|
||||
|
||||
declaration optionally declares a nonterminal as the start symbol.
|
||||
|
||||
In the grammar above,
|
||||
|
||||
\f(CWterm\fP
|
||||
|
||||
and
|
||||
|
||||
\f(CWnonterm\fP
|
||||
|
||||
denote identifiers that are terminals and nonterminals.
|
||||
|
||||
.PP
|
||||
|
||||
Rules define tree patterns in a fully parenthesized prefix
|
||||
|
||||
form. Every nonterminal denotes a tree.
|
||||
|
||||
Each operator has a fixed
|
||||
|
||||
arity, which is inferred from the rules in which it is used.
|
||||
|
||||
A chain rule is a rule whose pattern is another nonterminal.
|
||||
|
||||
If no start symbol is declared, the nonterminal defined by the first rule is used.
|
||||
|
||||
.PP
|
||||
|
||||
Each rule ends with an expression that computes the cost of matching
|
||||
|
||||
that rule; omitted costs
|
||||
|
||||
default to zero. Costs of chain rules must be constants.
|
||||
|
||||
.PP
|
||||
|
||||
The configuration section configures the output
|
||||
|
||||
for the trees being parsed and the client's environment.
|
||||
|
||||
As shown, this section must define
|
||||
|
||||
\f(CWNODEPTR_TYPE\fP
|
||||
|
||||
to be a visible typedef symbol for a pointer to a
|
||||
|
||||
node in the subject tree.
|
||||
|
||||
The labeller invokes
|
||||
|
||||
\f(CWOP_LABEL(p)\fP,
|
||||
|
||||
\f(CWLEFT\_CHILD(p)\fP, and
|
||||
|
||||
\f(CWRIGHT\_CHILD(p)\fP
|
||||
|
||||
to read the operator and children from the node pointed to by \f(CWp\fP.
|
||||
|
||||
If the configuration section defines these operations as macros, they are implemented in-line;
|
||||
|
||||
otherwise, they must be implemented as functions.
|
||||
|
||||
.PP
|
||||
|
||||
The matcher
|
||||
|
||||
computes and stores a single integral state in each node of the subject tree.
|
||||
|
||||
The configuration section must define a macro
|
||||
|
||||
\f(CWSTATE_LABEL(p)\fP
|
||||
|
||||
to access the state field of the node pointed to
|
||||
|
||||
by \f(CWp\fP. It must be large enough to hold a pointer, and
|
||||
|
||||
a macro is required because it is used as an lvalue.
|
||||
|
||||
.PP
|
||||
|
||||
.SH OPTIONS
|
||||
|
||||
.TP
|
||||
|
||||
.BI \-p \ prefix
|
||||
|
||||
.br
|
||||
|
||||
.ns
|
||||
|
||||
.TP
|
||||
|
||||
.BI \-p prefix
|
||||
|
||||
Use
|
||||
|
||||
.I prefix
|
||||
|
||||
as the disambiquating prefix for visible names and fields.
|
||||
|
||||
The default is `\f(CW_\fP'.
|
||||
|
||||
.TP
|
||||
|
||||
.B \-T
|
||||
|
||||
Arrange for
|
||||
|
||||
.sp
|
||||
|
||||
.nf
|
||||
|
||||
.ft CW
|
||||
|
||||
void _trace(NODEPTR_TYPE p, int eruleno,
|
||||
|
||||
int cost, int bestcost);
|
||||
|
||||
.sp
|
||||
|
||||
.fi
|
||||
|
||||
.ft R
|
||||
|
||||
to be called at each successful match.
|
||||
|
||||
\f(CWp\fP
|
||||
|
||||
identifies the node and
|
||||
|
||||
\f(CWeruleno\fP
|
||||
|
||||
identifies the matching rule; the rules are numbered
|
||||
|
||||
beginning at 1 in the order they appear in the input.
|
||||
|
||||
\f(CWcost\fP
|
||||
|
||||
is the cost of the match and
|
||||
|
||||
\f(CWbestcost\fP
|
||||
|
||||
is the cost of the best previous match. The current match
|
||||
|
||||
wins only if
|
||||
|
||||
\f(CWcost\fP
|
||||
|
||||
is less than \f(CWbestcost\fP.
|
||||
|
||||
32767 represents the infinite cost of no previous match.
|
||||
|
||||
\f(CW_trace\fP must be declared in the configuration section.
|
||||
|
||||
.SH "SEE ALSO"
|
||||
|
||||
.IR lcc (1)
|
||||
|
||||
.PP
|
||||
|
||||
C. W. Fraser and D. R. Hanson,
|
||||
|
||||
.IR A Retargetable C Compiler: Design and Implementation ,
|
||||
|
||||
Benjamin/Cummings, Redwood City, CA, 1995,
|
||||
|
||||
ISBN 0-8053-1670-1. Chapter 14.
|
||||
|
||||
.PP
|
||||
|
||||
C. W. Fraser, D. R. Hanson and T. A. Proebsting,
|
||||
|
||||
`Engineering a simple, efficient code generator generator,'
|
||||
|
||||
.I
|
||||
|
||||
ACM Letters on Programming Languages and Systems
|
||||
|
||||
.BR 1 ,
|
||||
|
||||
3 (Sep. 1992), 213-226.
|
||||
|
||||
.br
|
||||
|
||||
.SH BUGS
|
||||
|
||||
Mail bug reports along with the shortest input
|
||||
|
||||
that exposes them to drh@cs.princeton.edu.
|
||||
|
||||
682
src/cmd/lccom-1/lburg/lburg.c
Normal file
682
src/cmd/lccom-1/lburg/lburg.c
Normal file
@@ -0,0 +1,682 @@
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include "lburg.h"
|
||||
|
||||
static char rcsid[] = "$Id: lburg.c,v 2.10 2002/03/08 18:45:21 drh Exp $";
|
||||
static char *prefix = "";
|
||||
static int Tflag = 0;
|
||||
static int nflag = 0;
|
||||
static int ntnumber = 0;
|
||||
static Nonterm start = 0;
|
||||
static Term terms;
|
||||
static Nonterm nts;
|
||||
static Rule rules;
|
||||
static int nrules;
|
||||
|
||||
static struct block {
|
||||
struct block *link;
|
||||
} *memlist; /* list of allocated blocks */
|
||||
|
||||
static char *stringf(char *fmt, ...);
|
||||
static void print(char *fmt, ...);
|
||||
static void ckreach(Nonterm p);
|
||||
static void emitclosure(Nonterm nts);
|
||||
static void emitcost(Tree t, char *v);
|
||||
static void emitdefs(Nonterm nts, int ntnumber);
|
||||
static void emitheader(void);
|
||||
static void emitkids(Rule rules, int nrules);
|
||||
static void emitnts(Rule rules, int nrules);
|
||||
static void emitrecalc(char *pre, Term root, Term kid);
|
||||
static void emitrecord(char *pre, Rule r, char *c, int cost);
|
||||
static void emitrule(Nonterm nts);
|
||||
static void emitlabel(Term terms, Nonterm start, int ntnumber);
|
||||
static void emitstring(Rule rules);
|
||||
static void emitstruct(Nonterm nts, int ntnumber);
|
||||
static void emittest(Tree t, char *v, char *suffix);
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int c, i;
|
||||
Nonterm p;
|
||||
|
||||
for (i = 1; i < argc; i++)
|
||||
if (strcmp(argv[i], "-T") == 0)
|
||||
Tflag = 1;
|
||||
else if (strcmp(argv[i], "-n") == 0)
|
||||
nflag = 1;
|
||||
else if (strncmp(argv[i], "-p", 2) == 0 && argv[i][2])
|
||||
prefix = &argv[i][2];
|
||||
else if (strncmp(argv[i], "-p", 2) == 0 && i + 1 < argc)
|
||||
prefix = argv[++i];
|
||||
else if (*argv[i] == '-' && argv[i][1]) {
|
||||
yyerror("usage: %s [-T | -p prefix]... [ [ input ] output ] \n",
|
||||
argv[0]);
|
||||
exit(1);
|
||||
} else if (infp == NULL) {
|
||||
if (strcmp(argv[i], "-") == 0)
|
||||
infp = stdin;
|
||||
else if ((infp = fopen(argv[i], "r")) == NULL) {
|
||||
yyerror("%s: can't read `%s'\n", argv[0], argv[i]);
|
||||
exit(1);
|
||||
}
|
||||
} else if (outfp == NULL) {
|
||||
if (strcmp(argv[i], "-") == 0)
|
||||
outfp = stdout;
|
||||
if ((outfp = fopen(argv[i], "w")) == NULL) {
|
||||
yyerror("%s: can't write `%s'\n", argv[0], argv[i]);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (infp == NULL)
|
||||
infp = stdin;
|
||||
if (outfp == NULL)
|
||||
outfp = stdout;
|
||||
yyparse();
|
||||
if (start)
|
||||
ckreach(start);
|
||||
for (p = nts; p; p = p->link) {
|
||||
if (p->rules == NULL)
|
||||
yyerror("undefined nonterminal `%s'\n", p->name);
|
||||
if (!p->reached)
|
||||
yyerror("can't reach nonterminal `%s'\n", p->name);
|
||||
}
|
||||
emitheader();
|
||||
emitdefs(nts, ntnumber);
|
||||
emitstruct(nts, ntnumber);
|
||||
emitnts(rules, nrules);
|
||||
if (! nflag)
|
||||
emitstring(rules);
|
||||
emitrule(nts);
|
||||
emitclosure(nts);
|
||||
if (start)
|
||||
emitlabel(terms, start, ntnumber);
|
||||
emitkids(rules, nrules);
|
||||
if (!feof(infp))
|
||||
while ((c = getc(infp)) != EOF)
|
||||
putc(c, outfp);
|
||||
while (memlist) { /* for purify */
|
||||
struct block *q = memlist->link;
|
||||
free(memlist);
|
||||
memlist = q;
|
||||
}
|
||||
return errcnt > 0;
|
||||
}
|
||||
|
||||
/* alloc - allocate nbytes or issue fatal error */
|
||||
void *alloc(int nbytes) {
|
||||
struct block *p = calloc(1, sizeof *p + nbytes);
|
||||
|
||||
if (p == NULL) {
|
||||
yyerror("out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
p->link = memlist;
|
||||
memlist = p;
|
||||
return p + 1;
|
||||
}
|
||||
|
||||
/* stringf - format and save a string */
|
||||
static char *stringf(char *fmt, ...) {
|
||||
va_list ap;
|
||||
char buf[512];
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsprintf(buf, fmt, ap);
|
||||
va_end(ap);
|
||||
return strcpy(alloc(strlen(buf) + 1), buf);
|
||||
}
|
||||
|
||||
struct entry {
|
||||
union {
|
||||
char *name;
|
||||
struct term t;
|
||||
struct nonterm nt;
|
||||
} sym;
|
||||
struct entry *link;
|
||||
} *table[211];
|
||||
#define HASHSIZE (sizeof table/sizeof table[0])
|
||||
|
||||
/* hash - return hash number for str */
|
||||
static unsigned hash(char *str) {
|
||||
unsigned h = 0;
|
||||
|
||||
while (*str)
|
||||
h = (h<<1) + *str++;
|
||||
return h;
|
||||
}
|
||||
|
||||
/* lookup - lookup symbol name */
|
||||
static void *lookup(char *name) {
|
||||
struct entry *p = table[hash(name)%HASHSIZE];
|
||||
|
||||
for ( ; p; p = p->link)
|
||||
if (strcmp(name, p->sym.name) == 0)
|
||||
return &p->sym;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* install - install symbol name */
|
||||
static void *install(char *name) {
|
||||
struct entry *p = alloc(sizeof *p);
|
||||
int i = hash(name)%HASHSIZE;
|
||||
|
||||
p->sym.name = name;
|
||||
p->link = table[i];
|
||||
table[i] = p;
|
||||
return &p->sym;
|
||||
}
|
||||
|
||||
/* nonterm - create a new terminal id, if necessary */
|
||||
Nonterm nonterm(char *id) {
|
||||
Nonterm p = lookup(id), *q = &nts;
|
||||
|
||||
if (p && p->kind == NONTERM)
|
||||
return p;
|
||||
if (p && p->kind == TERM)
|
||||
yyerror("`%s' is a terminal\n", id);
|
||||
p = install(id);
|
||||
p->kind = NONTERM;
|
||||
p->number = ++ntnumber;
|
||||
if (p->number == 1)
|
||||
start = p;
|
||||
while (*q && (*q)->number < p->number)
|
||||
q = &(*q)->link;
|
||||
assert(*q == 0 || (*q)->number != p->number);
|
||||
p->link = *q;
|
||||
*q = p;
|
||||
return p;
|
||||
}
|
||||
|
||||
/* term - create a new terminal id with external symbol number esn */
|
||||
Term term(char *id, int esn) {
|
||||
Term p = lookup(id), *q = &terms;
|
||||
|
||||
if (p)
|
||||
yyerror("redefinition of terminal `%s'\n", id);
|
||||
else
|
||||
p = install(id);
|
||||
p->kind = TERM;
|
||||
p->esn = esn;
|
||||
p->arity = -1;
|
||||
while (*q && (*q)->esn < p->esn)
|
||||
q = &(*q)->link;
|
||||
if (*q && (*q)->esn == p->esn)
|
||||
yyerror("duplicate external symbol number `%s=%d'\n",
|
||||
p->name, p->esn);
|
||||
p->link = *q;
|
||||
*q = p;
|
||||
return p;
|
||||
}
|
||||
|
||||
/* tree - create & initialize a tree node with the given fields */
|
||||
Tree tree(char *id, Tree left, Tree right) {
|
||||
Tree t = alloc(sizeof *t);
|
||||
Term p = lookup(id);
|
||||
int arity = 0;
|
||||
|
||||
if (left && right)
|
||||
arity = 2;
|
||||
else if (left)
|
||||
arity = 1;
|
||||
if (p == NULL && arity > 0) {
|
||||
yyerror("undefined terminal `%s'\n", id);
|
||||
p = term(id, -1);
|
||||
} else if (p == NULL && arity == 0)
|
||||
p = (Term)nonterm(id);
|
||||
else if (p && p->kind == NONTERM && arity > 0) {
|
||||
yyerror("`%s' is a nonterminal\n", id);
|
||||
p = term(id, -1);
|
||||
}
|
||||
if (p->kind == TERM && p->arity == -1)
|
||||
p->arity = arity;
|
||||
if (p->kind == TERM && arity != p->arity)
|
||||
yyerror("inconsistent arity for terminal `%s'\n", id);
|
||||
t->op = p;
|
||||
t->nterms = p->kind == TERM;
|
||||
if ((t->left = left) != NULL)
|
||||
t->nterms += left->nterms;
|
||||
if ((t->right = right) != NULL)
|
||||
t->nterms += right->nterms;
|
||||
return t;
|
||||
}
|
||||
|
||||
/* rule - create & initialize a rule with the given fields */
|
||||
Rule rule(char *id, Tree pattern, char *template, char *code) {
|
||||
Rule r = alloc(sizeof *r), *q;
|
||||
Term p = pattern->op;
|
||||
char *end;
|
||||
|
||||
r->lhs = nonterm(id);
|
||||
r->packed = ++r->lhs->lhscount;
|
||||
for (q = &r->lhs->rules; *q; q = &(*q)->decode)
|
||||
;
|
||||
*q = r;
|
||||
r->pattern = pattern;
|
||||
r->ern = ++nrules;
|
||||
r->template = template;
|
||||
r->code = code;
|
||||
r->cost = strtol(code, &end, 10);
|
||||
if (*end) {
|
||||
r->cost = -1;
|
||||
r->code = stringf("(%s)", code);
|
||||
}
|
||||
if (p->kind == TERM) {
|
||||
for (q = &p->rules; *q; q = &(*q)->next)
|
||||
;
|
||||
*q = r;
|
||||
} else if (pattern->left == NULL && pattern->right == NULL) {
|
||||
Nonterm p = pattern->op;
|
||||
r->chain = p->chain;
|
||||
p->chain = r;
|
||||
if (r->cost == -1)
|
||||
yyerror("illegal nonconstant cost `%s'\n", code);
|
||||
}
|
||||
for (q = &rules; *q; q = &(*q)->link)
|
||||
;
|
||||
r->link = *q;
|
||||
*q = r;
|
||||
return r;
|
||||
}
|
||||
|
||||
/* print - formatted output */
|
||||
static void print(char *fmt, ...) {
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
for ( ; *fmt; fmt++)
|
||||
if (*fmt == '%')
|
||||
switch (*++fmt) {
|
||||
case 'd': fprintf(outfp, "%d", va_arg(ap, int)); break;
|
||||
case 's': fputs(va_arg(ap, char *), outfp); break;
|
||||
case 'P': fprintf(outfp, "%s_", prefix); break;
|
||||
case 'T': {
|
||||
Tree t = va_arg(ap, Tree);
|
||||
print("%S", t->op);
|
||||
if (t->left && t->right)
|
||||
print("(%T,%T)", t->left, t->right);
|
||||
else if (t->left)
|
||||
print("(%T)", t->left);
|
||||
break;
|
||||
}
|
||||
case 'R': {
|
||||
Rule r = va_arg(ap, Rule);
|
||||
print("%S: %T", r->lhs, r->pattern);
|
||||
break;
|
||||
}
|
||||
case 'S': {
|
||||
Term t = va_arg(ap, Term);
|
||||
fputs(t->name, outfp);
|
||||
break;
|
||||
}
|
||||
case '1': case '2': case '3': case '4': case '5': {
|
||||
int n = *fmt - '0';
|
||||
while (n-- > 0)
|
||||
putc('\t', outfp);
|
||||
break;
|
||||
}
|
||||
default: putc(*fmt, outfp); break;
|
||||
}
|
||||
else
|
||||
putc(*fmt, outfp);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/* reach - mark all nonterminals in tree t as reachable */
|
||||
static void reach(Tree t) {
|
||||
Nonterm p = t->op;
|
||||
|
||||
if (p->kind == NONTERM)
|
||||
if (!p->reached)
|
||||
ckreach(p);
|
||||
if (t->left)
|
||||
reach(t->left);
|
||||
if (t->right)
|
||||
reach(t->right);
|
||||
}
|
||||
|
||||
/* ckreach - mark all nonterminals reachable from p */
|
||||
static void ckreach(Nonterm p) {
|
||||
Rule r;
|
||||
|
||||
p->reached = 1;
|
||||
for (r = p->rules; r; r = r->decode)
|
||||
reach(r->pattern);
|
||||
}
|
||||
|
||||
/* emitcase - emit one case in function state */
|
||||
static void emitcase(Term p, int ntnumber) {
|
||||
Rule r;
|
||||
|
||||
print("%1case %d: /* %S */\n", p->esn, p);
|
||||
switch (p->arity) {
|
||||
case 0: case -1:
|
||||
break;
|
||||
case 1:
|
||||
print("%2%Plabel(LEFT_CHILD(a));\n");
|
||||
break;
|
||||
case 2:
|
||||
print("%2%Plabel(LEFT_CHILD(a));\n");
|
||||
print("%2%Plabel(RIGHT_CHILD(a));\n");
|
||||
break;
|
||||
default: assert(0);
|
||||
}
|
||||
for (r = p->rules; r; r = r->next) {
|
||||
char *indent = "\t\t\0";
|
||||
switch (p->arity) {
|
||||
case 0: case -1:
|
||||
print("%2/* %R */\n", r);
|
||||
if (r->cost == -1) {
|
||||
print("%2c = %s;\n", r->code);
|
||||
emitrecord("\t\t", r, "c", 0);
|
||||
} else
|
||||
emitrecord("\t\t", r, r->code, 0);
|
||||
break;
|
||||
case 1:
|
||||
if (r->pattern->nterms > 1) {
|
||||
print("%2if (%1/* %R */\n", r);
|
||||
emittest(r->pattern->left, "LEFT_CHILD(a)", " ");
|
||||
print("%2) {\n");
|
||||
indent = "\t\t\t";
|
||||
} else
|
||||
print("%2/* %R */\n", r);
|
||||
if (r->pattern->nterms == 2 && r->pattern->left
|
||||
&& r->pattern->right == NULL)
|
||||
emitrecalc(indent, r->pattern->op, r->pattern->left->op);
|
||||
print("%sc = ", indent);
|
||||
emitcost(r->pattern->left, "LEFT_CHILD(a)");
|
||||
print("%s;\n", r->code);
|
||||
emitrecord(indent, r, "c", 0);
|
||||
if (indent[2])
|
||||
print("%2}\n");
|
||||
break;
|
||||
case 2:
|
||||
if (r->pattern->nterms > 1) {
|
||||
print("%2if (%1/* %R */\n", r);
|
||||
emittest(r->pattern->left, "LEFT_CHILD(a)",
|
||||
r->pattern->right->nterms ? " && " : " ");
|
||||
emittest(r->pattern->right, "RIGHT_CHILD(a)", " ");
|
||||
print("%2) {\n");
|
||||
indent = "\t\t\t";
|
||||
} else
|
||||
print("%2/* %R */\n", r);
|
||||
print("%sc = ", indent);
|
||||
emitcost(r->pattern->left, "LEFT_CHILD(a)");
|
||||
emitcost(r->pattern->right, "RIGHT_CHILD(a)");
|
||||
print("%s;\n", r->code);
|
||||
emitrecord(indent, r, "c", 0);
|
||||
if (indent[2])
|
||||
print("%2}\n");
|
||||
break;
|
||||
default: assert(0);
|
||||
}
|
||||
}
|
||||
print("%2break;\n");
|
||||
}
|
||||
|
||||
/* emitclosure - emit the closure functions */
|
||||
static void emitclosure(Nonterm nts) {
|
||||
Nonterm p;
|
||||
|
||||
for (p = nts; p; p = p->link)
|
||||
if (p->chain)
|
||||
print("static void %Pclosure_%S(NODEPTR_TYPE, int);\n", p);
|
||||
print("\n");
|
||||
for (p = nts; p; p = p->link)
|
||||
if (p->chain) {
|
||||
Rule r;
|
||||
print("static void %Pclosure_%S(NODEPTR_TYPE a, int c) {\n"
|
||||
"%1struct %Pstate *p = STATE_LABEL(a);\n", p);
|
||||
for (r = p->chain; r; r = r->chain)
|
||||
emitrecord("\t", r, "c", r->cost);
|
||||
print("}\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* emitcost - emit cost computation for tree t */
|
||||
static void emitcost(Tree t, char *v) {
|
||||
Nonterm p = t->op;
|
||||
|
||||
if (p->kind == TERM) {
|
||||
if (t->left)
|
||||
emitcost(t->left, stringf("LEFT_CHILD(%s)", v));
|
||||
if (t->right)
|
||||
emitcost(t->right, stringf("RIGHT_CHILD(%s)", v));
|
||||
} else
|
||||
print("((struct %Pstate *)(%s->x.state))->cost[%P%S_NT] + ", v, p);
|
||||
}
|
||||
|
||||
/* emitdefs - emit nonterminal defines and data structures */
|
||||
static void emitdefs(Nonterm nts, int ntnumber) {
|
||||
Nonterm p;
|
||||
|
||||
for (p = nts; p; p = p->link)
|
||||
print("#define %P%S_NT %d\n", p, p->number);
|
||||
print("\n");
|
||||
if (! nflag) {
|
||||
print("static char *%Pntname[] = {\n%10,\n");
|
||||
for (p = nts; p; p = p->link)
|
||||
print("%1\"%S\",\n", p);
|
||||
print("%10\n};\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* emitheader - emit initial definitions */
|
||||
static void emitheader(void) {
|
||||
time_t timer = time(NULL);
|
||||
|
||||
print("/*\ngenerated at %sby %s\n*/\n", ctime(&timer), rcsid);
|
||||
print("static void %Pkids(NODEPTR_TYPE, int, NODEPTR_TYPE[]);\n");
|
||||
print("static void %Plabel(NODEPTR_TYPE);\n");
|
||||
print("static int %Prule(void*, int);\n\n");
|
||||
}
|
||||
|
||||
/* computekids - compute paths to kids in tree t */
|
||||
static char *computekids(Tree t, char *v, char *bp, int *ip) {
|
||||
Term p = t->op;
|
||||
|
||||
if (p->kind == NONTERM) {
|
||||
sprintf(bp, "\t\tkids[%d] = %s;\n", (*ip)++, v);
|
||||
bp += strlen(bp);
|
||||
} else if (p->arity > 0) {
|
||||
bp = computekids(t->left, stringf("LEFT_CHILD(%s)", v), bp, ip);
|
||||
if (p->arity == 2)
|
||||
bp = computekids(t->right, stringf("RIGHT_CHILD(%s)", v), bp, ip);
|
||||
}
|
||||
return bp;
|
||||
}
|
||||
|
||||
/* emitkids - emit _kids */
|
||||
static void emitkids(Rule rules, int nrules) {
|
||||
int i;
|
||||
Rule r, *rc = alloc((nrules + 1 + 1)*sizeof *rc);
|
||||
char **str = alloc((nrules + 1 + 1)*sizeof *str);
|
||||
|
||||
for (i = 0, r = rules; r; r = r->link) {
|
||||
int j = 0;
|
||||
char buf[1024], *bp = buf;
|
||||
*computekids(r->pattern, "p", bp, &j) = 0;
|
||||
for (j = 0; str[j] && strcmp(str[j], buf); j++)
|
||||
;
|
||||
if (str[j] == NULL)
|
||||
str[j] = strcpy(alloc(strlen(buf) + 1), buf);
|
||||
r->kids = rc[j];
|
||||
rc[j] = r;
|
||||
}
|
||||
print("static void %Pkids(NODEPTR_TYPE p, int eruleno, NODEPTR_TYPE kids[]) {\n"
|
||||
"%1if (!p)\n%2fatal(\"%Pkids\", \"Null tree\\n\", 0);\n"
|
||||
"%1if (!kids)\n%2fatal(\"%Pkids\", \"Null kids\\n\", 0);\n"
|
||||
"%1switch (eruleno) {\n");
|
||||
for (i = 0; (r = rc[i]) != NULL; i++) {
|
||||
for ( ; r; r = r->kids)
|
||||
print("%1case %d: /* %R */\n", r->ern, r);
|
||||
print("%s%2break;\n", str[i]);
|
||||
}
|
||||
print("%1default:\n%2fatal(\"%Pkids\", \"Bad rule number %%d\\n\", eruleno);\n%1}\n}\n\n");
|
||||
}
|
||||
|
||||
/* emitlabel - emit label function */
|
||||
static void emitlabel(Term terms, Nonterm start, int ntnumber) {
|
||||
int i;
|
||||
Term p;
|
||||
|
||||
print("static void %Plabel(NODEPTR_TYPE a) {\n%1int c;\n"
|
||||
"%1struct %Pstate *p;\n\n"
|
||||
"%1if (!a)\n%2fatal(\"%Plabel\", \"Null tree\\n\", 0);\n");
|
||||
print("%1STATE_LABEL(a) = p = allocate(sizeof *p, FUNC);\n"
|
||||
"%1p->rule._stmt = 0;\n");
|
||||
for (i = 1; i <= ntnumber; i++)
|
||||
print("%1p->cost[%d] =\n", i);
|
||||
print("%20x7fff;\n%1switch (OP_LABEL(a)) {\n");
|
||||
for (p = terms; p; p = p->link)
|
||||
emitcase(p, ntnumber);
|
||||
print("%1default:\n"
|
||||
"%2fatal(\"%Plabel\", \"Bad terminal %%d\\n\", OP_LABEL(a));\n%1}\n}\n\n");
|
||||
}
|
||||
|
||||
/* computents - fill in bp with _nts vector for tree t */
|
||||
static char *computents(Tree t, char *bp) {
|
||||
if (t) {
|
||||
Nonterm p = t->op;
|
||||
if (p->kind == NONTERM) {
|
||||
sprintf(bp, "%s_%s_NT, ", prefix, p->name);
|
||||
bp += strlen(bp);
|
||||
} else
|
||||
bp = computents(t->right, computents(t->left, bp));
|
||||
}
|
||||
return bp;
|
||||
}
|
||||
|
||||
/* emitnts - emit _nts ragged array */
|
||||
static void emitnts(Rule rules, int nrules) {
|
||||
Rule r;
|
||||
int i, j, *nts = alloc((nrules + 1)*sizeof *nts);
|
||||
char **str = alloc((nrules + 1)*sizeof *str);
|
||||
|
||||
for (i = 0, r = rules; r; r = r->link) {
|
||||
char buf[1024];
|
||||
*computents(r->pattern, buf) = 0;
|
||||
for (j = 0; str[j] && strcmp(str[j], buf); j++)
|
||||
;
|
||||
if (str[j] == NULL) {
|
||||
print("static short %Pnts_%d[] = { %s0 };\n", j, buf);
|
||||
str[j] = strcpy(alloc(strlen(buf) + 1), buf);
|
||||
}
|
||||
nts[i++] = j;
|
||||
}
|
||||
print("\nstatic short *%Pnts[] = {\n");
|
||||
for (i = j = 0, r = rules; r; r = r->link) {
|
||||
for ( ; j < r->ern; j++)
|
||||
print("%10,%1/* %d */\n", j);
|
||||
print("%1%Pnts_%d,%1/* %d */\n", nts[i++], j++);
|
||||
}
|
||||
print("};\n\n");
|
||||
}
|
||||
|
||||
/* emitrecalc - emit code that tests for recalculation of INDIR?(VREGP) */
|
||||
static void emitrecalc(char *pre, Term root, Term kid) {
|
||||
if (root->kind == TERM && strncmp(root->name, "INDIR", 5) == 0
|
||||
&& kid->kind == TERM && strcmp(kid->name, "VREGP" ) == 0) {
|
||||
Nonterm p;
|
||||
print("%sif (mayrecalc(a)) {\n", pre);
|
||||
print("%s%1struct %Pstate *q = a->syms[RX]->u.t.cse->x.state;\n", pre);
|
||||
for (p = nts; p; p = p->link) {
|
||||
print("%s%1if (q->cost[%P%S_NT] == 0) {\n", pre, p);
|
||||
print("%s%2p->cost[%P%S_NT] = 0;\n", pre, p);
|
||||
print("%s%2p->rule.%P%S = q->rule.%P%S;\n", pre, p, p);
|
||||
print("%s%1}\n", pre);
|
||||
}
|
||||
print("%s}\n", pre);
|
||||
}
|
||||
}
|
||||
|
||||
/* emitrecord - emit code that tests for a winning match of rule r */
|
||||
static void emitrecord(char *pre, Rule r, char *c, int cost) {
|
||||
if (Tflag)
|
||||
print("%s%Ptrace(a, %d, %s + %d, p->cost[%P%S_NT]);\n",
|
||||
pre, r->ern, c, cost, r->lhs);
|
||||
print("%sif (", pre);
|
||||
print("%s + %d < p->cost[%P%S_NT]) {\n"
|
||||
"%s%1p->cost[%P%S_NT] = %s + %d;\n%s%1p->rule.%P%S = %d;\n",
|
||||
c, cost, r->lhs, pre, r->lhs, c, cost, pre, r->lhs,
|
||||
r->packed);
|
||||
if (r->lhs->chain)
|
||||
print("%s%1%Pclosure_%S(a, %s + %d);\n", pre, r->lhs, c, cost);
|
||||
print("%s}\n", pre);
|
||||
}
|
||||
|
||||
/* emitrule - emit decoding vectors and _rule */
|
||||
static void emitrule(Nonterm nts) {
|
||||
Nonterm p;
|
||||
|
||||
for (p = nts; p; p = p->link) {
|
||||
Rule r;
|
||||
print("static short %Pdecode_%S[] = {\n%10,\n", p);
|
||||
for (r = p->rules; r; r = r->decode)
|
||||
print("%1%d,\n", r->ern);
|
||||
print("};\n\n");
|
||||
}
|
||||
print("static int %Prule(void *state, int goalnt) {\n"
|
||||
"%1if (goalnt < 1 || goalnt > %d)\n%2fatal(\"%Prule\", \"Bad goal nonterminal %%d\\n\", goalnt);\n"
|
||||
"%1if (!state)\n%2return 0;\n%1switch (goalnt) {\n", ntnumber);
|
||||
for (p = nts; p; p = p->link)
|
||||
print("%1case %P%S_NT:"
|
||||
"%1return %Pdecode_%S[((struct %Pstate *)state)->rule.%P%S];\n", p, p, p);
|
||||
print("%1default:\n%2fatal(\"%Prule\", \"Bad goal nonterminal %%d\\n\", goalnt);\n%2return 0;\n%1}\n}\n\n");
|
||||
}
|
||||
|
||||
/* emitstring - emit arrays of templates, instruction flags, and rules */
|
||||
static void emitstring(Rule rules) {
|
||||
Rule r;
|
||||
|
||||
print("static char *%Ptemplates[] = {\n");
|
||||
print("/* 0 */%10,\n");
|
||||
for (r = rules; r; r = r->link)
|
||||
print("/* %d */%1\"%s\",%1/* %R */\n", r->ern, r->template, r);
|
||||
print("};\n");
|
||||
print("\nstatic char %Pisinstruction[] = {\n");
|
||||
print("/* 0 */%10,\n");
|
||||
for (r = rules; r; r = r->link) {
|
||||
int len = strlen(r->template);
|
||||
print("/* %d */%1%d,%1/* %s */\n", r->ern,
|
||||
len >= 2 && r->template[len-2] == '\\' && r->template[len-1] == 'n',
|
||||
r->template);
|
||||
}
|
||||
print("};\n");
|
||||
print("\nstatic char *%Pstring[] = {\n");
|
||||
print("/* 0 */%10,\n");
|
||||
for (r = rules; r; r = r->link)
|
||||
print("/* %d */%1\"%R\",\n", r->ern, r);
|
||||
print("};\n\n");
|
||||
}
|
||||
|
||||
/* emitstruct - emit the definition of the state structure */
|
||||
static void emitstruct(Nonterm nts, int ntnumber) {
|
||||
print("struct %Pstate {\n%1short cost[%d];\n%1struct {\n", ntnumber + 1);
|
||||
for ( ; nts; nts = nts->link) {
|
||||
int n = 1, m = nts->lhscount;
|
||||
while ((m >>= 1) != 0)
|
||||
n++;
|
||||
print("%2unsigned int %P%S:%d;\n", nts, n);
|
||||
}
|
||||
print("%1} rule;\n};\n\n");
|
||||
}
|
||||
|
||||
/* emittest - emit clause for testing a match */
|
||||
static void emittest(Tree t, char *v, char *suffix) {
|
||||
Term p = t->op;
|
||||
|
||||
if (p->kind == TERM) {
|
||||
print("%3%s->op == %d%s/* %S */\n", v, p->esn,
|
||||
t->nterms > 1 ? " && " : suffix, p);
|
||||
if (t->left)
|
||||
emittest(t->left, stringf("LEFT_CHILD(%s)", v),
|
||||
t->right && t->right->nterms ? " && " : suffix);
|
||||
if (t->right)
|
||||
emittest(t->right, stringf("RIGHT_CHILD(%s)", v), suffix);
|
||||
}
|
||||
}
|
||||
132
src/cmd/lccom-1/lburg/lburg.h
Normal file
132
src/cmd/lccom-1/lburg/lburg.h
Normal file
@@ -0,0 +1,132 @@
|
||||
#ifndef BURG_INCLUDED
|
||||
|
||||
#define BURG_INCLUDED
|
||||
|
||||
|
||||
|
||||
/* $Id: lburg.h,v 2.1 1994/11/30 21:53:18 drh Exp $ */
|
||||
|
||||
/* iburg.c: */
|
||||
|
||||
extern void *alloc(int nbytes);
|
||||
|
||||
|
||||
|
||||
typedef enum { TERM=1, NONTERM } Kind;
|
||||
|
||||
typedef struct rule *Rule;
|
||||
|
||||
typedef struct term *Term;
|
||||
|
||||
struct term { /* terminals: */
|
||||
|
||||
char *name; /* terminal name */
|
||||
|
||||
Kind kind; /* TERM */
|
||||
|
||||
int esn; /* external symbol number */
|
||||
|
||||
int arity; /* operator arity */
|
||||
|
||||
Term link; /* next terminal in esn order */
|
||||
|
||||
Rule rules; /* rules whose pattern starts with term */
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
typedef struct nonterm *Nonterm;
|
||||
|
||||
struct nonterm { /* nonterminals: */
|
||||
|
||||
char *name; /* nonterminal name */
|
||||
|
||||
Kind kind; /* NONTERM */
|
||||
|
||||
int number; /* identifying number */
|
||||
|
||||
int lhscount; /* # times nt appears in a rule lhs */
|
||||
|
||||
int reached; /* 1 iff reached from start nonterminal */
|
||||
|
||||
Rule rules; /* rules w/nonterminal on lhs */
|
||||
|
||||
Rule chain; /* chain rules w/nonterminal on rhs */
|
||||
|
||||
Nonterm link; /* next terminal in number order */
|
||||
|
||||
};
|
||||
|
||||
extern Nonterm nonterm(char *id);
|
||||
|
||||
extern Term term(char *id, int esn);
|
||||
|
||||
|
||||
|
||||
typedef struct tree *Tree;
|
||||
|
||||
struct tree { /* tree patterns: */
|
||||
|
||||
void *op; /* a terminal or nonterminal */
|
||||
|
||||
Tree left, right; /* operands */
|
||||
|
||||
int nterms; /* number of terminal nodes in this tree */
|
||||
|
||||
};
|
||||
|
||||
extern Tree tree(char *op, Tree left, Tree right);
|
||||
|
||||
|
||||
|
||||
struct rule { /* rules: */
|
||||
|
||||
Nonterm lhs; /* lefthand side nonterminal */
|
||||
|
||||
Tree pattern; /* rule pattern */
|
||||
|
||||
int ern; /* external rule number */
|
||||
|
||||
int packed; /* packed external rule number */
|
||||
|
||||
int cost; /* cost, if a constant */
|
||||
|
||||
char *code; /* cost, if an expression */
|
||||
|
||||
char *template; /* assembler template */
|
||||
|
||||
Rule link; /* next rule in ern order */
|
||||
|
||||
Rule next; /* next rule with same pattern root */
|
||||
|
||||
Rule chain; /* next chain rule with same rhs */
|
||||
|
||||
Rule decode; /* next rule with same lhs */
|
||||
|
||||
Rule kids; /* next rule with same _kids pattern */
|
||||
|
||||
};
|
||||
|
||||
extern Rule rule(char *id, Tree pattern, char *template, char *code);
|
||||
|
||||
|
||||
|
||||
/* gram.y: */
|
||||
|
||||
void yyerror(char *fmt, ...);
|
||||
|
||||
int yyparse(void);
|
||||
|
||||
void yywarn(char *fmt, ...);
|
||||
|
||||
extern int errcnt;
|
||||
|
||||
extern FILE *infp;
|
||||
|
||||
extern FILE *outfp;
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
930
src/cmd/lccom-1/lex.c
Normal file
930
src/cmd/lccom-1/lex.c
Normal file
@@ -0,0 +1,930 @@
|
||||
#include "c.h"
|
||||
#include <float.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define MAXTOKEN 32
|
||||
|
||||
enum { BLANK=01, NEWLINE=02, LETTER=04,
|
||||
DIGIT=010, HEX=020, OTHER=040 };
|
||||
|
||||
static unsigned char map[256] = { /* 000 nul */ 0,
|
||||
/* 001 soh */ 0,
|
||||
/* 002 stx */ 0,
|
||||
/* 003 etx */ 0,
|
||||
/* 004 eot */ 0,
|
||||
/* 005 enq */ 0,
|
||||
/* 006 ack */ 0,
|
||||
/* 007 bel */ 0,
|
||||
/* 010 bs */ 0,
|
||||
/* 011 ht */ BLANK,
|
||||
/* 012 nl */ NEWLINE,
|
||||
/* 013 vt */ BLANK,
|
||||
/* 014 ff */ BLANK,
|
||||
/* 015 cr */ 0,
|
||||
/* 016 so */ 0,
|
||||
/* 017 si */ 0,
|
||||
/* 020 dle */ 0,
|
||||
/* 021 dc1 */ 0,
|
||||
/* 022 dc2 */ 0,
|
||||
/* 023 dc3 */ 0,
|
||||
/* 024 dc4 */ 0,
|
||||
/* 025 nak */ 0,
|
||||
/* 026 syn */ 0,
|
||||
/* 027 etb */ 0,
|
||||
/* 030 can */ 0,
|
||||
/* 031 em */ 0,
|
||||
/* 032 sub */ 0,
|
||||
/* 033 esc */ 0,
|
||||
/* 034 fs */ 0,
|
||||
/* 035 gs */ 0,
|
||||
/* 036 rs */ 0,
|
||||
/* 037 us */ 0,
|
||||
/* 040 sp */ BLANK,
|
||||
/* 041 ! */ OTHER,
|
||||
/* 042 " */ OTHER,
|
||||
/* 043 # */ OTHER,
|
||||
/* 044 $ */ 0,
|
||||
/* 045 % */ OTHER,
|
||||
/* 046 & */ OTHER,
|
||||
/* 047 ' */ OTHER,
|
||||
/* 050 ( */ OTHER,
|
||||
/* 051 ) */ OTHER,
|
||||
/* 052 * */ OTHER,
|
||||
/* 053 + */ OTHER,
|
||||
/* 054 , */ OTHER,
|
||||
/* 055 - */ OTHER,
|
||||
/* 056 . */ OTHER,
|
||||
/* 057 / */ OTHER,
|
||||
/* 060 0 */ DIGIT,
|
||||
/* 061 1 */ DIGIT,
|
||||
/* 062 2 */ DIGIT,
|
||||
/* 063 3 */ DIGIT,
|
||||
/* 064 4 */ DIGIT,
|
||||
/* 065 5 */ DIGIT,
|
||||
/* 066 6 */ DIGIT,
|
||||
/* 067 7 */ DIGIT,
|
||||
/* 070 8 */ DIGIT,
|
||||
/* 071 9 */ DIGIT,
|
||||
/* 072 : */ OTHER,
|
||||
/* 073 ; */ OTHER,
|
||||
/* 074 < */ OTHER,
|
||||
/* 075 = */ OTHER,
|
||||
/* 076 > */ OTHER,
|
||||
/* 077 ? */ OTHER,
|
||||
/* 100 @ */ 0,
|
||||
/* 101 A */ LETTER|HEX,
|
||||
/* 102 B */ LETTER|HEX,
|
||||
/* 103 C */ LETTER|HEX,
|
||||
/* 104 D */ LETTER|HEX,
|
||||
/* 105 E */ LETTER|HEX,
|
||||
/* 106 F */ LETTER|HEX,
|
||||
/* 107 G */ LETTER,
|
||||
/* 110 H */ LETTER,
|
||||
/* 111 I */ LETTER,
|
||||
/* 112 J */ LETTER,
|
||||
/* 113 K */ LETTER,
|
||||
/* 114 L */ LETTER,
|
||||
/* 115 M */ LETTER,
|
||||
/* 116 N */ LETTER,
|
||||
/* 117 O */ LETTER,
|
||||
/* 120 P */ LETTER,
|
||||
/* 121 Q */ LETTER,
|
||||
/* 122 R */ LETTER,
|
||||
/* 123 S */ LETTER,
|
||||
/* 124 T */ LETTER,
|
||||
/* 125 U */ LETTER,
|
||||
/* 126 V */ LETTER,
|
||||
/* 127 W */ LETTER,
|
||||
/* 130 X */ LETTER,
|
||||
/* 131 Y */ LETTER,
|
||||
/* 132 Z */ LETTER,
|
||||
/* 133 [ */ OTHER,
|
||||
/* 134 \ */ OTHER,
|
||||
/* 135 ] */ OTHER,
|
||||
/* 136 ^ */ OTHER,
|
||||
/* 137 _ */ LETTER,
|
||||
/* 140 ` */ 0,
|
||||
/* 141 a */ LETTER|HEX,
|
||||
/* 142 b */ LETTER|HEX,
|
||||
/* 143 c */ LETTER|HEX,
|
||||
/* 144 d */ LETTER|HEX,
|
||||
/* 145 e */ LETTER|HEX,
|
||||
/* 146 f */ LETTER|HEX,
|
||||
/* 147 g */ LETTER,
|
||||
/* 150 h */ LETTER,
|
||||
/* 151 i */ LETTER,
|
||||
/* 152 j */ LETTER,
|
||||
/* 153 k */ LETTER,
|
||||
/* 154 l */ LETTER,
|
||||
/* 155 m */ LETTER,
|
||||
/* 156 n */ LETTER,
|
||||
/* 157 o */ LETTER,
|
||||
/* 160 p */ LETTER,
|
||||
/* 161 q */ LETTER,
|
||||
/* 162 r */ LETTER,
|
||||
/* 163 s */ LETTER,
|
||||
/* 164 t */ LETTER,
|
||||
/* 165 u */ LETTER,
|
||||
/* 166 v */ LETTER,
|
||||
/* 167 w */ LETTER,
|
||||
/* 170 x */ LETTER,
|
||||
/* 171 y */ LETTER,
|
||||
/* 172 z */ LETTER,
|
||||
/* 173 { */ OTHER,
|
||||
/* 174 | */ OTHER,
|
||||
/* 175 } */ OTHER,
|
||||
/* 176 ~ */ OTHER, };
|
||||
static struct symbol tval;
|
||||
static char cbuf[BUFSIZE+1];
|
||||
static unsigned int wcbuf[BUFSIZE+1];
|
||||
|
||||
Coordinate src; /* current source coordinate */
|
||||
int t;
|
||||
char *token; /* current token */
|
||||
Symbol tsym; /* symbol table entry for current token */
|
||||
|
||||
static void *cput(int c, void *cl);
|
||||
static void *wcput(int c, void *cl);
|
||||
static void *scon(int q, void *put(int c, void *cl), void *cl);
|
||||
static int backslash(int q);
|
||||
static Symbol fcon(void);
|
||||
static Symbol icon(unsigned long, int, int);
|
||||
static void ppnumber(char *);
|
||||
|
||||
int gettok(void) {
|
||||
for (;;) {
|
||||
register unsigned char *rcp = cp;
|
||||
while (map[*rcp]&BLANK)
|
||||
rcp++;
|
||||
if (limit - rcp < MAXTOKEN) {
|
||||
cp = rcp;
|
||||
fillbuf();
|
||||
rcp = cp;
|
||||
}
|
||||
src.file = file;
|
||||
src.x = (char *)rcp - line;
|
||||
src.y = lineno;
|
||||
cp = rcp + 1;
|
||||
switch (*rcp++) {
|
||||
case '/': if (*rcp == '*') {
|
||||
int c = 0;
|
||||
for (rcp++; *rcp != '/' || c != '*'; )
|
||||
if (map[*rcp]&NEWLINE) {
|
||||
if (rcp < limit)
|
||||
c = *rcp;
|
||||
cp = rcp + 1;
|
||||
nextline();
|
||||
rcp = cp;
|
||||
if (rcp == limit)
|
||||
break;
|
||||
} else
|
||||
c = *rcp++;
|
||||
if (rcp < limit)
|
||||
rcp++;
|
||||
else
|
||||
error("unclosed comment\n");
|
||||
cp = rcp;
|
||||
continue;
|
||||
}
|
||||
return '/';
|
||||
case '<':
|
||||
if (*rcp == '=') return cp++, LEQ;
|
||||
if (*rcp == '<') return cp++, LSHIFT;
|
||||
return '<';
|
||||
case '>':
|
||||
if (*rcp == '=') return cp++, GEQ;
|
||||
if (*rcp == '>') return cp++, RSHIFT;
|
||||
return '>';
|
||||
case '-':
|
||||
if (*rcp == '>') return cp++, DEREF;
|
||||
if (*rcp == '-') return cp++, DECR;
|
||||
return '-';
|
||||
case '=': return *rcp == '=' ? cp++, EQL : '=';
|
||||
case '!': return *rcp == '=' ? cp++, NEQ : '!';
|
||||
case '|': return *rcp == '|' ? cp++, OROR : '|';
|
||||
case '&': return *rcp == '&' ? cp++, ANDAND : '&';
|
||||
case '+': return *rcp == '+' ? cp++, INCR : '+';
|
||||
case ';': case ',': case ':':
|
||||
case '*': case '~': case '%': case '^': case '?':
|
||||
case '[': case ']': case '{': case '}': case '(': case ')':
|
||||
return rcp[-1];
|
||||
case '\n': case '\v': case '\r': case '\f':
|
||||
nextline();
|
||||
if (cp == limit) {
|
||||
tsym = NULL;
|
||||
return EOI;
|
||||
}
|
||||
continue;
|
||||
|
||||
case 'i':
|
||||
if (rcp[0] == 'f'
|
||||
&& !(map[rcp[1]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 1;
|
||||
return IF;
|
||||
}
|
||||
if (rcp[0] == 'n'
|
||||
&& rcp[1] == 't'
|
||||
&& !(map[rcp[2]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 2;
|
||||
tsym = inttype->u.sym;
|
||||
return INT;
|
||||
}
|
||||
goto id;
|
||||
case 'h': case 'j': case 'k': case 'm': case 'n': case 'o':
|
||||
case 'p': case 'q': case 'x': case 'y': case 'z':
|
||||
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
|
||||
case 'G': case 'H': case 'I': case 'J': case 'K':
|
||||
case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
|
||||
case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
|
||||
case 'Y': case 'Z':
|
||||
id:
|
||||
if (limit - rcp < MAXLINE) {
|
||||
cp = rcp - 1;
|
||||
fillbuf();
|
||||
rcp = ++cp;
|
||||
}
|
||||
assert(cp == rcp);
|
||||
token = (char *)rcp - 1;
|
||||
while (map[*rcp]&(DIGIT|LETTER))
|
||||
rcp++;
|
||||
token = stringn(token, (char *)rcp - token);
|
||||
tsym = lookup(token, identifiers);
|
||||
cp = rcp;
|
||||
return ID;
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9': {
|
||||
unsigned long n = 0;
|
||||
if (limit - rcp < MAXLINE) {
|
||||
cp = rcp - 1;
|
||||
fillbuf();
|
||||
rcp = ++cp;
|
||||
}
|
||||
assert(cp == rcp);
|
||||
token = (char *)rcp - 1;
|
||||
if (*token == '0' && (*rcp == 'x' || *rcp == 'X')) {
|
||||
int d, overflow = 0;
|
||||
while (*++rcp) {
|
||||
if (map[*rcp]&DIGIT)
|
||||
d = *rcp - '0';
|
||||
else if (*rcp >= 'a' && *rcp <= 'f')
|
||||
d = *rcp - 'a' + 10;
|
||||
else if (*rcp >= 'A' && *rcp <= 'F')
|
||||
d = *rcp - 'A' + 10;
|
||||
else
|
||||
break;
|
||||
if (n&~(~0UL >> 4))
|
||||
overflow = 1;
|
||||
else
|
||||
n = (n<<4) + d;
|
||||
}
|
||||
if ((char *)rcp - token <= 2)
|
||||
error("invalid hexadecimal constant `%S'\n", token, (char *)rcp-token);
|
||||
cp = rcp;
|
||||
tsym = icon(n, overflow, 16);
|
||||
} else if (*token == '0') {
|
||||
int err = 0, overflow = 0;
|
||||
for ( ; map[*rcp]&DIGIT; rcp++) {
|
||||
if (*rcp == '8' || *rcp == '9')
|
||||
err = 1;
|
||||
if (n&~(~0UL >> 3))
|
||||
overflow = 1;
|
||||
else
|
||||
n = (n<<3) + (*rcp - '0');
|
||||
}
|
||||
if (*rcp == '.' || *rcp == 'e' || *rcp == 'E') {
|
||||
cp = rcp;
|
||||
tsym = fcon();
|
||||
return FCON;
|
||||
}
|
||||
cp = rcp;
|
||||
tsym = icon(n, overflow, 8);
|
||||
if (err)
|
||||
error("invalid octal constant `%S'\n", token, (char*)cp-token);
|
||||
} else {
|
||||
int overflow = 0;
|
||||
for (n = *token - '0'; map[*rcp]&DIGIT; ) {
|
||||
int d = *rcp++ - '0';
|
||||
if (n > (ULONG_MAX - d)/10)
|
||||
overflow = 1;
|
||||
else
|
||||
n = 10*n + d;
|
||||
}
|
||||
if (*rcp == '.' || *rcp == 'e' || *rcp == 'E') {
|
||||
cp = rcp;
|
||||
tsym = fcon();
|
||||
return FCON;
|
||||
}
|
||||
cp = rcp;
|
||||
tsym = icon(n, overflow, 10);
|
||||
}
|
||||
return ICON;
|
||||
}
|
||||
case '.':
|
||||
if (rcp[0] == '.' && rcp[1] == '.') {
|
||||
cp += 2;
|
||||
return ELLIPSIS;
|
||||
}
|
||||
if ((map[*rcp]&DIGIT) == 0)
|
||||
return '.';
|
||||
if (limit - rcp < MAXLINE) {
|
||||
cp = rcp - 1;
|
||||
fillbuf();
|
||||
rcp = ++cp;
|
||||
}
|
||||
assert(cp == rcp);
|
||||
cp = rcp - 1;
|
||||
token = (char *)cp;
|
||||
tsym = fcon();
|
||||
return FCON;
|
||||
case 'L':
|
||||
if (*rcp == '\'') {
|
||||
unsigned int *s = scon(*cp, wcput, wcbuf);
|
||||
if (s - wcbuf > 2)
|
||||
warning("excess characters in wide-character literal ignored\n");
|
||||
tval.type = widechar;
|
||||
tval.u.c.v.u = wcbuf[0];
|
||||
tsym = &tval;
|
||||
return ICON;
|
||||
} else if (*rcp == '"') {
|
||||
unsigned int *s = scon(*cp, wcput, wcbuf);
|
||||
tval.type = array(widechar, s - wcbuf, 0);
|
||||
tval.u.c.v.p = wcbuf;
|
||||
tsym = &tval;
|
||||
return SCON;
|
||||
} else
|
||||
goto id;
|
||||
case '\'': {
|
||||
char *s = scon(*--cp, cput, cbuf);
|
||||
if (s - cbuf > 2)
|
||||
warning("excess characters in multibyte character literal ignored\n");
|
||||
tval.type = inttype;
|
||||
if (chartype->op == INT)
|
||||
tval.u.c.v.i = extend(cbuf[0], chartype);
|
||||
else
|
||||
tval.u.c.v.i = cbuf[0]&0xFF;
|
||||
tsym = &tval;
|
||||
return ICON;
|
||||
}
|
||||
case '"': {
|
||||
char *s = scon(*--cp, cput, cbuf);
|
||||
tval.type = array(chartype, s - cbuf, 0);
|
||||
tval.u.c.v.p = cbuf;
|
||||
tsym = &tval;
|
||||
return SCON;
|
||||
}
|
||||
case 'a':
|
||||
if (rcp[0] == 'u'
|
||||
&& rcp[1] == 't'
|
||||
&& rcp[2] == 'o'
|
||||
&& !(map[rcp[3]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 3;
|
||||
return AUTO;
|
||||
}
|
||||
goto id;
|
||||
case 'b':
|
||||
if (rcp[0] == 'r'
|
||||
&& rcp[1] == 'e'
|
||||
&& rcp[2] == 'a'
|
||||
&& rcp[3] == 'k'
|
||||
&& !(map[rcp[4]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 4;
|
||||
return BREAK;
|
||||
}
|
||||
goto id;
|
||||
case 'c':
|
||||
if (rcp[0] == 'a'
|
||||
&& rcp[1] == 's'
|
||||
&& rcp[2] == 'e'
|
||||
&& !(map[rcp[3]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 3;
|
||||
return CASE;
|
||||
}
|
||||
if (rcp[0] == 'h'
|
||||
&& rcp[1] == 'a'
|
||||
&& rcp[2] == 'r'
|
||||
&& !(map[rcp[3]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 3;
|
||||
tsym = chartype->u.sym;
|
||||
return CHAR;
|
||||
}
|
||||
if (rcp[0] == 'o'
|
||||
&& rcp[1] == 'n'
|
||||
&& rcp[2] == 's'
|
||||
&& rcp[3] == 't'
|
||||
&& !(map[rcp[4]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 4;
|
||||
return CONST;
|
||||
}
|
||||
if (rcp[0] == 'o'
|
||||
&& rcp[1] == 'n'
|
||||
&& rcp[2] == 't'
|
||||
&& rcp[3] == 'i'
|
||||
&& rcp[4] == 'n'
|
||||
&& rcp[5] == 'u'
|
||||
&& rcp[6] == 'e'
|
||||
&& !(map[rcp[7]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 7;
|
||||
return CONTINUE;
|
||||
}
|
||||
goto id;
|
||||
case 'd':
|
||||
if (rcp[0] == 'e'
|
||||
&& rcp[1] == 'f'
|
||||
&& rcp[2] == 'a'
|
||||
&& rcp[3] == 'u'
|
||||
&& rcp[4] == 'l'
|
||||
&& rcp[5] == 't'
|
||||
&& !(map[rcp[6]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 6;
|
||||
return DEFAULT;
|
||||
}
|
||||
if (rcp[0] == 'o'
|
||||
&& rcp[1] == 'u'
|
||||
&& rcp[2] == 'b'
|
||||
&& rcp[3] == 'l'
|
||||
&& rcp[4] == 'e'
|
||||
&& !(map[rcp[5]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 5;
|
||||
tsym = doubletype->u.sym;
|
||||
return DOUBLE;
|
||||
}
|
||||
if (rcp[0] == 'o'
|
||||
&& !(map[rcp[1]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 1;
|
||||
return DO;
|
||||
}
|
||||
goto id;
|
||||
case 'e':
|
||||
if (rcp[0] == 'l'
|
||||
&& rcp[1] == 's'
|
||||
&& rcp[2] == 'e'
|
||||
&& !(map[rcp[3]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 3;
|
||||
return ELSE;
|
||||
}
|
||||
if (rcp[0] == 'n'
|
||||
&& rcp[1] == 'u'
|
||||
&& rcp[2] == 'm'
|
||||
&& !(map[rcp[3]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 3;
|
||||
return ENUM;
|
||||
}
|
||||
if (rcp[0] == 'x'
|
||||
&& rcp[1] == 't'
|
||||
&& rcp[2] == 'e'
|
||||
&& rcp[3] == 'r'
|
||||
&& rcp[4] == 'n'
|
||||
&& !(map[rcp[5]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 5;
|
||||
return EXTERN;
|
||||
}
|
||||
goto id;
|
||||
case 'f':
|
||||
if (rcp[0] == 'l'
|
||||
&& rcp[1] == 'o'
|
||||
&& rcp[2] == 'a'
|
||||
&& rcp[3] == 't'
|
||||
&& !(map[rcp[4]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 4;
|
||||
tsym = floattype->u.sym;
|
||||
return FLOAT;
|
||||
}
|
||||
if (rcp[0] == 'o'
|
||||
&& rcp[1] == 'r'
|
||||
&& !(map[rcp[2]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 2;
|
||||
return FOR;
|
||||
}
|
||||
goto id;
|
||||
case 'g':
|
||||
if (rcp[0] == 'o'
|
||||
&& rcp[1] == 't'
|
||||
&& rcp[2] == 'o'
|
||||
&& !(map[rcp[3]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 3;
|
||||
return GOTO;
|
||||
}
|
||||
goto id;
|
||||
case 'l':
|
||||
if (rcp[0] == 'o'
|
||||
&& rcp[1] == 'n'
|
||||
&& rcp[2] == 'g'
|
||||
&& !(map[rcp[3]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 3;
|
||||
return LONG;
|
||||
}
|
||||
goto id;
|
||||
case 'r':
|
||||
if (rcp[0] == 'e'
|
||||
&& rcp[1] == 'g'
|
||||
&& rcp[2] == 'i'
|
||||
&& rcp[3] == 's'
|
||||
&& rcp[4] == 't'
|
||||
&& rcp[5] == 'e'
|
||||
&& rcp[6] == 'r'
|
||||
&& !(map[rcp[7]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 7;
|
||||
return REGISTER;
|
||||
}
|
||||
if (rcp[0] == 'e'
|
||||
&& rcp[1] == 't'
|
||||
&& rcp[2] == 'u'
|
||||
&& rcp[3] == 'r'
|
||||
&& rcp[4] == 'n'
|
||||
&& !(map[rcp[5]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 5;
|
||||
return RETURN;
|
||||
}
|
||||
goto id;
|
||||
case 's':
|
||||
if (rcp[0] == 'h'
|
||||
&& rcp[1] == 'o'
|
||||
&& rcp[2] == 'r'
|
||||
&& rcp[3] == 't'
|
||||
&& !(map[rcp[4]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 4;
|
||||
return SHORT;
|
||||
}
|
||||
if (rcp[0] == 'i'
|
||||
&& rcp[1] == 'g'
|
||||
&& rcp[2] == 'n'
|
||||
&& rcp[3] == 'e'
|
||||
&& rcp[4] == 'd'
|
||||
&& !(map[rcp[5]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 5;
|
||||
return SIGNED;
|
||||
}
|
||||
if (rcp[0] == 'i'
|
||||
&& rcp[1] == 'z'
|
||||
&& rcp[2] == 'e'
|
||||
&& rcp[3] == 'o'
|
||||
&& rcp[4] == 'f'
|
||||
&& !(map[rcp[5]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 5;
|
||||
return SIZEOF;
|
||||
}
|
||||
if (rcp[0] == 't'
|
||||
&& rcp[1] == 'a'
|
||||
&& rcp[2] == 't'
|
||||
&& rcp[3] == 'i'
|
||||
&& rcp[4] == 'c'
|
||||
&& !(map[rcp[5]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 5;
|
||||
return STATIC;
|
||||
}
|
||||
if (rcp[0] == 't'
|
||||
&& rcp[1] == 'r'
|
||||
&& rcp[2] == 'u'
|
||||
&& rcp[3] == 'c'
|
||||
&& rcp[4] == 't'
|
||||
&& !(map[rcp[5]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 5;
|
||||
return STRUCT;
|
||||
}
|
||||
if (rcp[0] == 'w'
|
||||
&& rcp[1] == 'i'
|
||||
&& rcp[2] == 't'
|
||||
&& rcp[3] == 'c'
|
||||
&& rcp[4] == 'h'
|
||||
&& !(map[rcp[5]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 5;
|
||||
return SWITCH;
|
||||
}
|
||||
goto id;
|
||||
case 't':
|
||||
if (rcp[0] == 'y'
|
||||
&& rcp[1] == 'p'
|
||||
&& rcp[2] == 'e'
|
||||
&& rcp[3] == 'd'
|
||||
&& rcp[4] == 'e'
|
||||
&& rcp[5] == 'f'
|
||||
&& !(map[rcp[6]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 6;
|
||||
return TYPEDEF;
|
||||
}
|
||||
goto id;
|
||||
case 'u':
|
||||
if (rcp[0] == 'n'
|
||||
&& rcp[1] == 'i'
|
||||
&& rcp[2] == 'o'
|
||||
&& rcp[3] == 'n'
|
||||
&& !(map[rcp[4]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 4;
|
||||
return UNION;
|
||||
}
|
||||
if (rcp[0] == 'n'
|
||||
&& rcp[1] == 's'
|
||||
&& rcp[2] == 'i'
|
||||
&& rcp[3] == 'g'
|
||||
&& rcp[4] == 'n'
|
||||
&& rcp[5] == 'e'
|
||||
&& rcp[6] == 'd'
|
||||
&& !(map[rcp[7]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 7;
|
||||
return UNSIGNED;
|
||||
}
|
||||
goto id;
|
||||
case 'v':
|
||||
if (rcp[0] == 'o'
|
||||
&& rcp[1] == 'i'
|
||||
&& rcp[2] == 'd'
|
||||
&& !(map[rcp[3]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 3;
|
||||
tsym = voidtype->u.sym;
|
||||
return VOID;
|
||||
}
|
||||
if (rcp[0] == 'o'
|
||||
&& rcp[1] == 'l'
|
||||
&& rcp[2] == 'a'
|
||||
&& rcp[3] == 't'
|
||||
&& rcp[4] == 'i'
|
||||
&& rcp[5] == 'l'
|
||||
&& rcp[6] == 'e'
|
||||
&& !(map[rcp[7]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 7;
|
||||
return VOLATILE;
|
||||
}
|
||||
goto id;
|
||||
case 'w':
|
||||
if (rcp[0] == 'h'
|
||||
&& rcp[1] == 'i'
|
||||
&& rcp[2] == 'l'
|
||||
&& rcp[3] == 'e'
|
||||
&& !(map[rcp[4]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 4;
|
||||
return WHILE;
|
||||
}
|
||||
goto id;
|
||||
case '_':
|
||||
if (rcp[0] == '_'
|
||||
&& rcp[1] == 't'
|
||||
&& rcp[2] == 'y'
|
||||
&& rcp[3] == 'p'
|
||||
&& rcp[4] == 'e'
|
||||
&& rcp[5] == 'c'
|
||||
&& rcp[6] == 'o'
|
||||
&& rcp[7] == 'd'
|
||||
&& rcp[8] == 'e'
|
||||
&& !(map[rcp[9]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 9;
|
||||
return TYPECODE;
|
||||
}
|
||||
if (rcp[0] == '_'
|
||||
&& rcp[1] == 'f'
|
||||
&& rcp[2] == 'i'
|
||||
&& rcp[3] == 'r'
|
||||
&& rcp[4] == 's'
|
||||
&& rcp[5] == 't'
|
||||
&& rcp[6] == 'a'
|
||||
&& rcp[7] == 'r'
|
||||
&& rcp[8] == 'g'
|
||||
&& !(map[rcp[9]]&(DIGIT|LETTER))) {
|
||||
cp = rcp + 9;
|
||||
return FIRSTARG;
|
||||
}
|
||||
goto id;
|
||||
default:
|
||||
if ((map[cp[-1]]&BLANK) == 0) {
|
||||
if (cp[-1] < ' ' || cp[-1] >= 0177)
|
||||
error("illegal character `\\0%o'\n", cp[-1]);
|
||||
else
|
||||
error("illegal character `%c'\n", cp[-1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Symbol icon(unsigned long n, int overflow, int base)
|
||||
{
|
||||
if (((*cp=='u'||*cp=='U') && (cp[1]=='l'||cp[1]=='L'))
|
||||
|| ((*cp=='l'||*cp=='L') && (cp[1]=='u'||cp[1]=='U'))) {
|
||||
tval.type = unsignedlong;
|
||||
cp += 2;
|
||||
} else if (*cp == 'u' || *cp == 'U') {
|
||||
if (overflow || n > unsignedtype->u.sym->u.limits.max.i)
|
||||
tval.type = unsignedlong;
|
||||
else
|
||||
tval.type = unsignedtype;
|
||||
cp += 1;
|
||||
} else if (*cp == 'l' || *cp == 'L') {
|
||||
if (overflow || n > longtype->u.sym->u.limits.max.i)
|
||||
tval.type = unsignedlong;
|
||||
else
|
||||
tval.type = longtype;
|
||||
cp += 1;
|
||||
} else if (overflow || n > longtype->u.sym->u.limits.max.i) {
|
||||
tval.type = unsignedlong;
|
||||
} else if (n > inttype->u.sym->u.limits.max.i) {
|
||||
tval.type = longtype;
|
||||
} else if (base != 10 && n > inttype->u.sym->u.limits.max.i) {
|
||||
tval.type = unsignedtype;
|
||||
} else
|
||||
tval.type = inttype;
|
||||
|
||||
switch (tval.type->op) {
|
||||
case INT:
|
||||
if (overflow || n > tval.type->u.sym->u.limits.max.i) {
|
||||
warning("overflow in constant `%S'\n", token,
|
||||
(char*)cp - token);
|
||||
tval.u.c.v.i = tval.type->u.sym->u.limits.max.i;
|
||||
} else
|
||||
tval.u.c.v.i = n;
|
||||
break;
|
||||
case UNSIGNED:
|
||||
if (overflow || n > tval.type->u.sym->u.limits.max.u) {
|
||||
warning("overflow in constant `%S'\n", token,
|
||||
(char*)cp - token);
|
||||
tval.u.c.v.u = tval.type->u.sym->u.limits.max.u;
|
||||
} else
|
||||
tval.u.c.v.u = n;
|
||||
break;
|
||||
default: assert(0);
|
||||
}
|
||||
ppnumber("integer");
|
||||
return &tval;
|
||||
}
|
||||
static void ppnumber(char *which) {
|
||||
unsigned char *rcp = cp--;
|
||||
|
||||
for ( ; (map[*cp]&(DIGIT|LETTER)) || *cp == '.'; cp++)
|
||||
if ((cp[0] == 'E' || cp[0] == 'e')
|
||||
&& (cp[1] == '-' || cp[1] == '+'))
|
||||
cp++;
|
||||
if (cp > rcp)
|
||||
error("`%S' is a preprocessing number but an invalid %s constant\n", token,
|
||||
|
||||
(char*)cp-token, which);
|
||||
}
|
||||
static Symbol fcon(void) {
|
||||
if (*cp == '.')
|
||||
do
|
||||
cp++;
|
||||
while (map[*cp]&DIGIT);
|
||||
if (*cp == 'e' || *cp == 'E') {
|
||||
if (*++cp == '-' || *cp == '+')
|
||||
cp++;
|
||||
if (map[*cp]&DIGIT)
|
||||
do
|
||||
cp++;
|
||||
while (map[*cp]&DIGIT);
|
||||
else
|
||||
error("invalid floating constant `%S'\n", token,
|
||||
(char*)cp - token);
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
tval.u.c.v.d = strtod(token, NULL);
|
||||
if (errno == ERANGE)
|
||||
warning("overflow in floating constant `%S'\n", token,
|
||||
(char*)cp - token);
|
||||
if (*cp == 'f' || *cp == 'F') {
|
||||
++cp;
|
||||
if (tval.u.c.v.d > floattype->u.sym->u.limits.max.d)
|
||||
warning("overflow in floating constant `%S'\n", token,
|
||||
(char*)cp - token);
|
||||
tval.type = floattype;
|
||||
} else if (*cp == 'l' || *cp == 'L') {
|
||||
cp++;
|
||||
tval.type = longdouble;
|
||||
} else {
|
||||
if (tval.u.c.v.d > doubletype->u.sym->u.limits.max.d)
|
||||
warning("overflow in floating constant `%S'\n", token,
|
||||
(char*)cp - token);
|
||||
tval.type = doubletype;
|
||||
}
|
||||
ppnumber("floating");
|
||||
return &tval;
|
||||
}
|
||||
|
||||
static void *cput(int c, void *cl) {
|
||||
char *s = cl;
|
||||
|
||||
if (c < 0 || c > 255)
|
||||
warning("overflow in escape sequence with resulting value `%d'\n", c);
|
||||
*s++ = c;
|
||||
return s;
|
||||
}
|
||||
|
||||
static void *wcput(int c, void *cl) {
|
||||
unsigned int *s = cl;
|
||||
|
||||
*s++ = c;
|
||||
return s;
|
||||
}
|
||||
|
||||
static void *scon(int q, void *put(int c, void *cl), void *cl) {
|
||||
int n = 0, nbad = 0;
|
||||
|
||||
do {
|
||||
cp++;
|
||||
while (*cp != q) {
|
||||
int c;
|
||||
if (map[*cp]&NEWLINE) {
|
||||
if (cp < limit)
|
||||
break;
|
||||
cp++;
|
||||
nextline();
|
||||
if (cp == limit)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
c = *cp++;
|
||||
if (c == '\\') {
|
||||
if (map[*cp]&NEWLINE) {
|
||||
if (cp++ < limit)
|
||||
continue;
|
||||
nextline();
|
||||
}
|
||||
if (limit - cp < MAXTOKEN)
|
||||
fillbuf();
|
||||
c = backslash(q);
|
||||
} else if (c < 0 || c > 255 || map[c] == 0)
|
||||
nbad++;
|
||||
if (n++ < BUFSIZE)
|
||||
cl = put(c, cl);
|
||||
}
|
||||
if (*cp == q)
|
||||
cp++;
|
||||
else
|
||||
error("missing %c\n", q);
|
||||
if (q == '"' && put == wcput && getchr() == 'L') {
|
||||
if (limit - cp < 2)
|
||||
fillbuf();
|
||||
if (cp[1] == '"')
|
||||
cp++;
|
||||
}
|
||||
} while (q == '"' && getchr() == '"');
|
||||
cl = put(0, cl);
|
||||
if (n >= BUFSIZE)
|
||||
error("%s literal too long\n", q == '"' ? "string" : "character");
|
||||
if (Aflag >= 2 && q == '"' && n > 509)
|
||||
warning("more than 509 characters in a string literal\n");
|
||||
if (Aflag >= 2 && nbad > 0)
|
||||
warning("%s literal contains non-portable characters\n",
|
||||
q == '"' ? "string" : "character");
|
||||
return cl;
|
||||
}
|
||||
int getchr(void) {
|
||||
for (;;) {
|
||||
while (map[*cp]&BLANK)
|
||||
cp++;
|
||||
if (!(map[*cp]&NEWLINE))
|
||||
return *cp;
|
||||
cp++;
|
||||
nextline();
|
||||
if (cp == limit)
|
||||
return EOI;
|
||||
}
|
||||
}
|
||||
static int backslash(int q) {
|
||||
unsigned int c;
|
||||
|
||||
switch (*cp++) {
|
||||
case 'a': return 7;
|
||||
case 'b': return '\b';
|
||||
case 'f': return '\f';
|
||||
case 'n': return '\n';
|
||||
case 'r': return '\r';
|
||||
case 't': return '\t';
|
||||
case 'v': return '\v';
|
||||
case '\'': case '"': case '\\': case '\?': break;
|
||||
case 'x': {
|
||||
int overflow = 0;
|
||||
if ((map[*cp]&(DIGIT|HEX)) == 0) {
|
||||
if (*cp < ' ' || *cp == 0177)
|
||||
error("ill-formed hexadecimal escape sequence\n");
|
||||
else
|
||||
error("ill-formed hexadecimal escape sequence `\\x%c'\n", *cp);
|
||||
if (*cp != q)
|
||||
cp++;
|
||||
return 0;
|
||||
}
|
||||
for (c = 0; map[*cp]&(DIGIT|HEX); cp++) {
|
||||
if (c >> (8*widechar->size - 4))
|
||||
overflow = 1;
|
||||
if (map[*cp]&DIGIT)
|
||||
c = (c<<4) + *cp - '0';
|
||||
else
|
||||
c = (c<<4) + (*cp&~040) - 'A' + 10;
|
||||
}
|
||||
if (overflow)
|
||||
warning("overflow in hexadecimal escape sequence\n");
|
||||
return c&ones(8*widechar->size);
|
||||
}
|
||||
case '0': case '1': case '2': case '3':
|
||||
case '4': case '5': case '6': case '7':
|
||||
c = *(cp-1) - '0';
|
||||
if (*cp >= '0' && *cp <= '7') {
|
||||
c = (c<<3) + *cp++ - '0';
|
||||
if (*cp >= '0' && *cp <= '7')
|
||||
c = (c<<3) + *cp++ - '0';
|
||||
}
|
||||
return c;
|
||||
default:
|
||||
if (cp[-1] < ' ' || cp[-1] >= 0177)
|
||||
warning("unrecognized character escape sequence\n");
|
||||
else
|
||||
warning("unrecognized character escape sequence `\\%c'\n", cp[-1]);
|
||||
}
|
||||
return cp[-1];
|
||||
}
|
||||
19
src/cmd/lccom-1/lib/assert.c
Normal file
19
src/cmd/lccom-1/lib/assert.c
Normal file
@@ -0,0 +1,19 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifndef EXPORT
|
||||
# define EXPORT
|
||||
#endif
|
||||
|
||||
EXPORT int _assert(char *e, char *file, int line)
|
||||
{
|
||||
fprintf(stderr, "assertion failed:");
|
||||
if (e)
|
||||
fprintf(stderr, " %s", e);
|
||||
if (file)
|
||||
fprintf(stderr, " file %s", file);
|
||||
fprintf(stderr, " line %d\n", line);
|
||||
fflush(stderr);
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
136
src/cmd/lccom-1/lib/bbexit.c
Normal file
136
src/cmd/lccom-1/lib/bbexit.c
Normal file
@@ -0,0 +1,136 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifndef EXPORT
|
||||
# define EXPORT
|
||||
#endif
|
||||
|
||||
EXPORT struct callsite {
|
||||
char *file, *name;
|
||||
union coordinate {
|
||||
struct { unsigned int index:6,x:10,y:16; } be;
|
||||
struct { unsigned int y:16,x:10,index:6; } le;
|
||||
unsigned int coord;
|
||||
} u;
|
||||
} *_caller, **_callerp = &_caller;
|
||||
|
||||
EXPORT void _setcallerp(struct callsite **p)
|
||||
{
|
||||
_callerp = p;
|
||||
}
|
||||
|
||||
static struct _bbdata {
|
||||
struct _bbdata *link;
|
||||
int npoints, *counts;
|
||||
union coordinate *coords;
|
||||
char **files;
|
||||
struct func {
|
||||
struct func *link;
|
||||
struct caller {
|
||||
struct caller *link;
|
||||
struct callsite *caller;
|
||||
int count;
|
||||
} *callers;
|
||||
char *name;
|
||||
union coordinate src;
|
||||
} *funcs;
|
||||
} tail, *_bblist = &tail;
|
||||
|
||||
static void unpack(unsigned int coord, int *index, int *x, int *y)
|
||||
{
|
||||
static union { int x; char endian; } little = { 1 };
|
||||
union coordinate u;
|
||||
|
||||
u.coord = coord;
|
||||
if (little.endian) {
|
||||
*index = u.le.index;
|
||||
*x = u.le.x;
|
||||
*y = u.le.y;
|
||||
} else {
|
||||
*index = u.be.index;
|
||||
*x = u.be.x;
|
||||
*y = u.be.y;
|
||||
}
|
||||
}
|
||||
|
||||
static void profout(struct _bbdata *p, FILE *fp)
|
||||
{
|
||||
int i, index, x, y;
|
||||
struct func *f;
|
||||
struct caller *q;
|
||||
|
||||
for (i = 0; p->files[i]; i++)
|
||||
;
|
||||
fprintf(fp, "%d\n", i);
|
||||
for (i = 0; p->files[i]; i++)
|
||||
fprintf(fp, "%s\n", p->files[i]);
|
||||
for (i = 0, f = p->funcs; f; i++, f = f->link)
|
||||
if ((q = f->callers))
|
||||
for (i--; q; q = q->link)
|
||||
i++;
|
||||
fprintf(fp, "%d\n", i);
|
||||
for (f = p->funcs; f; f = f->link) {
|
||||
int n = 0;
|
||||
for (q = f->callers; q; n += q->count, q = q->link) {
|
||||
unpack(f->src.coord, &index, &x, &y);
|
||||
fprintf(fp, "%s %d %d %d %d", f->name, index, x, y, q->count);
|
||||
if (q->caller) {
|
||||
unpack(q->caller->u.coord, &index, &x, &y);
|
||||
fprintf(fp, " %s %s %d %d\n", q->caller->name, q->caller->file, x, y);
|
||||
} else
|
||||
fprintf(fp, " ? ? 0 0\n");
|
||||
}
|
||||
if (n == 0) {
|
||||
unpack(f->src.coord, &index, &x, &y);
|
||||
fprintf(fp, "%s %d %d %d 0 ? ? 0 0\n", f->name, index, x, y);
|
||||
}
|
||||
}
|
||||
fprintf(fp, "%d\n", p->npoints);
|
||||
for (i = 0; i < p->npoints; i++) {
|
||||
unpack(p->coords[i].coord, &index, &x, &y);
|
||||
fprintf(fp, "%d %d %d %d\n", index, x, y, p->counts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void bbexit(void)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
if (_bblist != &tail && (fp = fopen("prof.out", "a"))) {
|
||||
for ( ; _bblist != &tail; _bblist = _bblist->link)
|
||||
profout(_bblist, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT void _epilogue(struct func *callee)
|
||||
{
|
||||
*_callerp = 0;
|
||||
}
|
||||
|
||||
EXPORT void _prologue(struct func *callee, struct _bbdata *yylink)
|
||||
{
|
||||
static struct caller callers[4096];
|
||||
static int next;
|
||||
struct caller *p;
|
||||
|
||||
if (!yylink->link) {
|
||||
yylink->link = _bblist;
|
||||
_bblist = yylink;
|
||||
if (next == 0)
|
||||
atexit(bbexit);
|
||||
}
|
||||
for (p = callee->callers; p; p = p->link)
|
||||
if (p->caller == *_callerp) {
|
||||
p->count++;
|
||||
break;
|
||||
}
|
||||
if (!p && next < sizeof callers/sizeof callers[0]) {
|
||||
p = &callers[next++];
|
||||
p->caller = *_callerp;
|
||||
p->count = 1;
|
||||
p->link = callee->callers;
|
||||
callee->callers = p;
|
||||
}
|
||||
*_callerp = 0;
|
||||
}
|
||||
16
src/cmd/lccom-1/lib/yynull.c
Normal file
16
src/cmd/lccom-1/lib/yynull.c
Normal file
@@ -0,0 +1,16 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifndef EXPORT
|
||||
# define EXPORT
|
||||
#endif
|
||||
|
||||
EXPORT void _YYnull(char *file, int line)
|
||||
{
|
||||
fprintf(stderr, "null pointer dereferenced:");
|
||||
if (file)
|
||||
fprintf(stderr, " file %s,", file);
|
||||
fprintf(stderr, " line %d\n", line);
|
||||
fflush(stderr);
|
||||
abort();
|
||||
}
|
||||
55
src/cmd/lccom-1/list.c
Normal file
55
src/cmd/lccom-1/list.c
Normal file
@@ -0,0 +1,55 @@
|
||||
#include "c.h"
|
||||
|
||||
static List freenodes; /* free list nodes */
|
||||
|
||||
/* append - append x to list, return new list */
|
||||
List append(void *x, List list) {
|
||||
List new;
|
||||
|
||||
if ((new = freenodes) != NULL)
|
||||
freenodes = freenodes->link;
|
||||
else
|
||||
NEW(new, PERM);
|
||||
if (list) {
|
||||
new->link = list->link;
|
||||
list->link = new;
|
||||
} else
|
||||
new->link = new;
|
||||
new->x = x;
|
||||
return new;
|
||||
}
|
||||
|
||||
/* length - # elements in list */
|
||||
int length(List list) {
|
||||
int n = 0;
|
||||
|
||||
if (list) {
|
||||
List lp = list;
|
||||
do
|
||||
n++;
|
||||
while ((lp = lp->link) != list);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/* ltov - convert list to an NULL-terminated vector allocated in arena */
|
||||
void *ltov(List *list, unsigned arena) {
|
||||
int i = 0;
|
||||
void **array = newarray(length(*list) + 1, sizeof array[0], arena);
|
||||
|
||||
if (*list) {
|
||||
List lp = *list;
|
||||
do {
|
||||
lp = lp->link;
|
||||
array[i++] = lp->x;
|
||||
} while (lp != *list);
|
||||
#ifndef PURIFY
|
||||
lp = (*list)->link;
|
||||
(*list)->link = freenodes;
|
||||
freenodes = lp;
|
||||
#endif
|
||||
}
|
||||
*list = NULL;
|
||||
array[i] = NULL;
|
||||
return array;
|
||||
}
|
||||
245
src/cmd/lccom-1/main.c
Normal file
245
src/cmd/lccom-1/main.c
Normal file
@@ -0,0 +1,245 @@
|
||||
#include "c.h"
|
||||
|
||||
static const char *version = "RetroBSD revision " VERSION;
|
||||
|
||||
static void typestab(Symbol, void *);
|
||||
|
||||
static void stabline(Coordinate *);
|
||||
static void stabend(Coordinate *, Symbol, Coordinate **, Symbol *, Symbol *);
|
||||
Interface *IR = NULL;
|
||||
|
||||
int Aflag; /* >= 0 if -A specified */
|
||||
int Pflag; /* != 0 if -P specified */
|
||||
int glevel; /* == [0-9] if -g[0-9] specified */
|
||||
int xref; /* != 0 for cross-reference data */
|
||||
Symbol YYnull; /* _YYnull symbol if -n or -nvalidate specified */
|
||||
Symbol YYcheck; /* _YYcheck symbol if -nvalidate,check specified */
|
||||
|
||||
static char *comment;
|
||||
static Interface stabIR;
|
||||
static char *currentfile; /* current file name */
|
||||
static int currentline; /* current line number */
|
||||
static FILE *srcfp; /* stream for current file, if non-NULL */
|
||||
static int srcpos; /* position of srcfp, if srcfp is non-NULL */
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = argc - 1; i > 0; i--)
|
||||
if (strncmp(argv[i], "-target=", 8) == 0)
|
||||
break;
|
||||
if (i > 0) {
|
||||
char *s = strchr(argv[i], '\\');
|
||||
if (s != NULL)
|
||||
*s = '/';
|
||||
for (j = 0; bindings[j].name && bindings[j].ir; j++)
|
||||
if (strcmp(&argv[i][8], bindings[j].name) == 0) {
|
||||
IR = bindings[j].ir;
|
||||
break;
|
||||
}
|
||||
if (s != NULL)
|
||||
*s = '\\';
|
||||
}
|
||||
if (!IR) {
|
||||
fprint(stderr, "%s: unknown target", argv[0]);
|
||||
if (i > 0)
|
||||
fprint(stderr, " `%s'", &argv[i][8]);
|
||||
fprint(stderr, "; must specify one of\n");
|
||||
for (i = 0; bindings[i].name; i++)
|
||||
fprint(stderr, "\t-target=%s\n", bindings[i].name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
init(argc, argv);
|
||||
t = gettok();
|
||||
(*IR->progbeg)(argc, argv);
|
||||
for (i = 1; i < argc; i++)
|
||||
if (strcmp(argv[i], "-n") == 0) {
|
||||
if (!YYnull) {
|
||||
YYnull = install(string("_YYnull"), &globals, GLOBAL, PERM);
|
||||
YYnull->type = func(voidptype, NULL, 1);
|
||||
YYnull->sclass = EXTERN;
|
||||
(*IR->defsymbol)(YYnull);
|
||||
}
|
||||
} else if (strncmp(argv[i], "-n", 2) == 0) { /* -nvalid[,check] */
|
||||
char *p = strchr(argv[i], ',');
|
||||
if (p) {
|
||||
YYcheck = install(string(p+1), &globals, GLOBAL, PERM);
|
||||
YYcheck->type = func(voidptype, NULL, 1);
|
||||
YYcheck->sclass = EXTERN;
|
||||
(*IR->defsymbol)(YYcheck);
|
||||
p = stringn(argv[i]+2, p - (argv[i]+2));
|
||||
} else
|
||||
p = string(argv[i]+2);
|
||||
YYnull = install(p, &globals, GLOBAL, PERM);
|
||||
YYnull->type = func(voidptype, NULL, 1);
|
||||
YYnull->sclass = EXTERN;
|
||||
(*IR->defsymbol)(YYnull);
|
||||
} else {
|
||||
profInit(argv[i]);
|
||||
traceInit(argv[i]);
|
||||
}
|
||||
if (glevel && IR->stabinit)
|
||||
(*IR->stabinit)(firstfile, argc, argv);
|
||||
program();
|
||||
if (events.end)
|
||||
apply(events.end, NULL, NULL);
|
||||
memset(&events, 0, sizeof events);
|
||||
if (glevel || xref) {
|
||||
Symbol symroot = NULL;
|
||||
Coordinate src;
|
||||
foreach(types, GLOBAL, typestab, &symroot);
|
||||
foreach(identifiers, GLOBAL, typestab, &symroot);
|
||||
src.file = firstfile;
|
||||
src.x = 0;
|
||||
src.y = lineno;
|
||||
if ((glevel > 2 || xref) && IR->stabend)
|
||||
(*IR->stabend)(&src, symroot,
|
||||
ltov(&loci, PERM),
|
||||
ltov(&symbols, PERM), NULL);
|
||||
else if (IR->stabend)
|
||||
(*IR->stabend)(&src, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
finalize();
|
||||
(*IR->progend)();
|
||||
deallocate(PERM);
|
||||
return errcnt > 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* main_init - process program arguments
|
||||
*/
|
||||
void main_init(int argc, char *argv[])
|
||||
{
|
||||
char *infile = NULL, *outfile = NULL;
|
||||
int i;
|
||||
static int inited;
|
||||
|
||||
if (inited)
|
||||
return;
|
||||
inited = 1;
|
||||
type_init(argc, argv);
|
||||
for (i = 1; i < argc; i++)
|
||||
if (strcmp(argv[i], "-g") == 0 || strcmp(argv[i], "-g2") == 0)
|
||||
glevel = 2;
|
||||
else if (strncmp(argv[i], "-g", 2) == 0) { /* -gn[,x] */
|
||||
char *p = strchr(argv[i], ',');
|
||||
glevel = atoi(argv[i]+2);
|
||||
if (p) {
|
||||
comment = p + 1;
|
||||
if (glevel == 0)
|
||||
glevel = 1;
|
||||
if (stabIR.stabline == NULL) {
|
||||
stabIR.stabline = IR->stabline;
|
||||
stabIR.stabend = IR->stabend;
|
||||
IR->stabline = stabline;
|
||||
IR->stabend = stabend;
|
||||
}
|
||||
}
|
||||
} else if (strcmp(argv[i], "-x") == 0)
|
||||
xref++;
|
||||
else if (strcmp(argv[i], "-A") == 0) {
|
||||
++Aflag;
|
||||
} else if (strcmp(argv[i], "-P") == 0)
|
||||
Pflag++;
|
||||
else if (strcmp(argv[i], "-w") == 0)
|
||||
wflag++;
|
||||
else if (strcmp(argv[i], "-v") == 0)
|
||||
fprint(stderr, "%s %s\n", argv[0], version);
|
||||
else if (strncmp(argv[i], "-s", 2) == 0)
|
||||
density = strtod(&argv[i][2], NULL);
|
||||
else if (strncmp(argv[i], "-errout=", 8) == 0) {
|
||||
FILE *f = fopen(argv[i]+8, "w");
|
||||
if (f == NULL) {
|
||||
fprint(stderr, "%s: can't write errors to `%s'\n", argv[0], argv[i]+8);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
fclose(f);
|
||||
f = freopen(argv[i]+8, "w", stderr);
|
||||
assert(f);
|
||||
} else if (strncmp(argv[i], "-e", 2) == 0) {
|
||||
int x;
|
||||
if ((x = strtol(&argv[i][2], NULL, 0)) > 0)
|
||||
errlimit = x;
|
||||
} else if (strncmp(argv[i], "-little_endian=", 15) == 0)
|
||||
IR->little_endian = argv[i][15] - '0';
|
||||
else if (strncmp(argv[i], "-mulops_calls=", 18) == 0)
|
||||
IR->mulops_calls = argv[i][18] - '0';
|
||||
else if (strncmp(argv[i], "-wants_callb=", 13) == 0)
|
||||
IR->wants_callb = argv[i][13] - '0';
|
||||
else if (strncmp(argv[i], "-wants_argb=", 12) == 0)
|
||||
IR->wants_argb = argv[i][12] - '0';
|
||||
else if (strncmp(argv[i], "-left_to_right=", 15) == 0)
|
||||
IR->left_to_right = argv[i][15] - '0';
|
||||
else if (strncmp(argv[i], "-wants_dag=", 11) == 0)
|
||||
IR->wants_dag = argv[i][11] - '0';
|
||||
else if (*argv[i] != '-' || strcmp(argv[i], "-") == 0) {
|
||||
if (infile == NULL)
|
||||
infile = argv[i];
|
||||
else if (outfile == NULL)
|
||||
outfile = argv[i];
|
||||
}
|
||||
|
||||
if (infile != NULL && strcmp(infile, "-") != 0
|
||||
&& freopen(infile, "r", stdin) == NULL) {
|
||||
fprint(stderr, "%s: can't read `%s'\n", argv[0], infile);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (outfile != NULL && strcmp(outfile, "-") != 0
|
||||
&& freopen(outfile, "w", stdout) == NULL) {
|
||||
fprint(stderr, "%s: can't write `%s'\n", argv[0], outfile);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* typestab - emit stab entries for p
|
||||
*/
|
||||
static void typestab(Symbol p, void *cl)
|
||||
{
|
||||
if (*(Symbol *)cl == 0 && p->sclass && p->sclass != TYPEDEF)
|
||||
*(Symbol *)cl = p;
|
||||
if ((p->sclass == TYPEDEF || p->sclass == 0) && IR->stabtype)
|
||||
(*IR->stabtype)(p);
|
||||
}
|
||||
|
||||
/*
|
||||
* stabline - emit source code for source coordinate *cp
|
||||
*/
|
||||
static void stabline(Coordinate *cp)
|
||||
{
|
||||
if (cp->file && cp->file != currentfile) {
|
||||
if (srcfp)
|
||||
fclose(srcfp);
|
||||
currentfile = cp->file;
|
||||
srcfp = fopen(currentfile, "r");
|
||||
srcpos = 0;
|
||||
currentline = 0;
|
||||
}
|
||||
if (currentline != cp->y && srcfp) {
|
||||
char buf[512];
|
||||
if (srcpos > cp->y) {
|
||||
rewind(srcfp);
|
||||
srcpos = 0;
|
||||
}
|
||||
for ( ; srcpos < cp->y; srcpos++)
|
||||
if (fgets(buf, sizeof buf, srcfp) == NULL) {
|
||||
fclose(srcfp);
|
||||
srcfp = NULL;
|
||||
break;
|
||||
}
|
||||
if (srcfp && srcpos == cp->y)
|
||||
print("%s%s", comment, buf);
|
||||
}
|
||||
currentline = cp->y;
|
||||
if (stabIR.stabline)
|
||||
(*stabIR.stabline)(cp);
|
||||
}
|
||||
|
||||
static void stabend(Coordinate *cp, Symbol p, Coordinate **cpp, Symbol *sp, Symbol *stab)
|
||||
{
|
||||
if (stabIR.stabend)
|
||||
(*stabIR.stabend)(cp, p, cpp, sp, stab);
|
||||
if (srcfp)
|
||||
fclose(srcfp);
|
||||
}
|
||||
1143
src/cmd/lccom-1/mips.md
Normal file
1143
src/cmd/lccom-1/mips.md
Normal file
File diff suppressed because it is too large
Load Diff
73
src/cmd/lccom-1/null.c
Normal file
73
src/cmd/lccom-1/null.c
Normal file
@@ -0,0 +1,73 @@
|
||||
#include "c.h"
|
||||
#define I(f) null_##f
|
||||
|
||||
static Node I(gen)(Node p) { return p; }
|
||||
static void I(address)(Symbol q, Symbol p, long n) {}
|
||||
static void I(blockbeg)(Env *e) {}
|
||||
static void I(blockend)(Env *e) {}
|
||||
static void I(defaddress)(Symbol p) {}
|
||||
static void I(defconst)(int suffix, int size, Value v) {}
|
||||
static void I(defstring)(int len, char *s) {}
|
||||
static void I(defsymbol)(Symbol p) {}
|
||||
static void I(emit)(Node p) {}
|
||||
static void I(export)(Symbol p) {}
|
||||
static void I(function)(Symbol f, Symbol caller[], Symbol callee[], int ncalls) {}
|
||||
static void I(global)(Symbol p) {}
|
||||
static void I(import)(Symbol p) {}
|
||||
static void I(local)(Symbol p) {}
|
||||
static void I(progbeg)(int argc, char *argv[]) {}
|
||||
static void I(progend)(void) {}
|
||||
static void I(segment)(int s) {}
|
||||
static void I(space)(int n) {}
|
||||
static void I(stabblock)(int brace, int lev, Symbol *p) {}
|
||||
static void I(stabend)(Coordinate *cp, Symbol p, Coordinate **cpp, Symbol *sp, Symbol *stab) {}
|
||||
static void I(stabfend)(Symbol p, int lineno) {}
|
||||
static void I(stabinit)(char *file, int argc, char *argv[]) {}
|
||||
static void I(stabline)(Coordinate *cp) {}
|
||||
static void I(stabsym)(Symbol p) {}
|
||||
static void I(stabtype)(Symbol p) {}
|
||||
|
||||
Interface nullIR = {
|
||||
{ 1, 1, 0 }, /* char */
|
||||
{ 2, 2, 0 }, /* short */
|
||||
{ 4, 4, 0 }, /* int */
|
||||
{ 8, 8, 1 }, /* long */
|
||||
{ 8 ,8, 1 }, /* long long */
|
||||
{ 4, 4, 1 }, /* float */
|
||||
{ 8, 8, 1 }, /* double */
|
||||
{ 16,16,1 }, /* long double */
|
||||
{ 4, 4, 0 }, /* T* */
|
||||
{ 0, 4, 0 }, /* struct */
|
||||
1, /* little_endian */
|
||||
0, /* mulops_calls */
|
||||
0, /* wants_callb */
|
||||
0, /* wants_argb */
|
||||
1, /* left_to_right */
|
||||
0, /* wants_dag */
|
||||
0, /* unsigned_char */
|
||||
I(address),
|
||||
I(blockbeg),
|
||||
I(blockend),
|
||||
I(defaddress),
|
||||
I(defconst),
|
||||
I(defstring),
|
||||
I(defsymbol),
|
||||
I(emit),
|
||||
I(export),
|
||||
I(function),
|
||||
I(gen),
|
||||
I(global),
|
||||
I(import),
|
||||
I(local),
|
||||
I(progbeg),
|
||||
I(progend),
|
||||
I(segment),
|
||||
I(space),
|
||||
I(stabblock),
|
||||
I(stabend),
|
||||
I(stabfend),
|
||||
I(stabinit),
|
||||
I(stabline),
|
||||
I(stabsym),
|
||||
I(stabtype)
|
||||
};
|
||||
131
src/cmd/lccom-1/ops.h
Normal file
131
src/cmd/lccom-1/ops.h
Normal file
@@ -0,0 +1,131 @@
|
||||
gop(CNST,1)
|
||||
op(CNST,F,fdx)
|
||||
op(CNST,I,csilh)
|
||||
op(CNST,P,p)
|
||||
op(CNST,U,csilh)
|
||||
gop(ARG,2)
|
||||
op(ARG,B,-)
|
||||
op(ARG,F,fdx)
|
||||
op(ARG,I,ilh)
|
||||
op(ARG,P,p)
|
||||
op(ARG,U,ilh)
|
||||
gop(ASGN,3)
|
||||
op(ASGN,B,-)
|
||||
op(ASGN,F,fdx)
|
||||
op(ASGN,I,csilh)
|
||||
op(ASGN,P,p)
|
||||
op(ASGN,U,csilh)
|
||||
gop(INDIR,4)
|
||||
op(INDIR,B,-)
|
||||
op(INDIR,F,fdx)
|
||||
op(INDIR,I,csilh)
|
||||
op(INDIR,P,p)
|
||||
op(INDIR,U,csilh)
|
||||
gop(CVF,7)
|
||||
op(CVF,F,fdx)
|
||||
op(CVF,I,ilh)
|
||||
gop(CVI,8)
|
||||
op(CVI,F,fdx)
|
||||
op(CVI,I,csilh)
|
||||
op(CVI,U,csilhp)
|
||||
gop(CVP,9)
|
||||
op(CVP,U,p)
|
||||
gop(CVU,11)
|
||||
op(CVU,I,csilh)
|
||||
op(CVU,P,p)
|
||||
op(CVU,U,csilh)
|
||||
gop(NEG,12)
|
||||
op(NEG,F,fdx)
|
||||
op(NEG,I,ilh)
|
||||
gop(CALL,13)
|
||||
op(CALL,B,-)
|
||||
op(CALL,F,fdx)
|
||||
op(CALL,I,ilh)
|
||||
op(CALL,P,p)
|
||||
op(CALL,U,ilh)
|
||||
op(CALL,V,-)
|
||||
gop(RET,15)
|
||||
op(RET,F,fdx)
|
||||
op(RET,I,ilh)
|
||||
op(RET,P,p)
|
||||
op(RET,U,ilh)
|
||||
op(RET,V,-)
|
||||
gop(ADDRG,16)
|
||||
op(ADDRG,P,p)
|
||||
gop(ADDRF,17)
|
||||
op(ADDRF,P,p)
|
||||
gop(ADDRL,18)
|
||||
op(ADDRL,P,p)
|
||||
gop(ADD,19)
|
||||
op(ADD,F,fdx)
|
||||
op(ADD,I,ilh)
|
||||
op(ADD,P,p)
|
||||
op(ADD,U,ilhp)
|
||||
gop(SUB,20)
|
||||
op(SUB,F,fdx)
|
||||
op(SUB,I,ilh)
|
||||
op(SUB,P,p)
|
||||
op(SUB,U,ilhp)
|
||||
gop(LSH,21)
|
||||
op(LSH,I,ilh)
|
||||
op(LSH,U,ilh)
|
||||
gop(MOD,22)
|
||||
op(MOD,I,ilh)
|
||||
op(MOD,U,ilh)
|
||||
gop(RSH,23)
|
||||
op(RSH,I,ilh)
|
||||
op(RSH,U,ilh)
|
||||
gop(BAND,24)
|
||||
op(BAND,I,ilh)
|
||||
op(BAND,U,ilh)
|
||||
gop(BCOM,25)
|
||||
op(BCOM,I,ilh)
|
||||
op(BCOM,U,ilh)
|
||||
gop(BOR,26)
|
||||
op(BOR,I,ilh)
|
||||
op(BOR,U,ilh)
|
||||
gop(BXOR,27)
|
||||
op(BXOR,I,ilh)
|
||||
op(BXOR,U,ilh)
|
||||
gop(DIV,28)
|
||||
op(DIV,F,fdx)
|
||||
op(DIV,I,ilh)
|
||||
op(DIV,U,ilh)
|
||||
gop(MUL,29)
|
||||
op(MUL,F,fdx)
|
||||
op(MUL,I,ilh)
|
||||
op(MUL,U,ilh)
|
||||
gop(EQ,30)
|
||||
op(EQ,F,fdx)
|
||||
op(EQ,I,ilh)
|
||||
op(EQ,U,ilhp)
|
||||
gop(GE,31)
|
||||
op(GE,F,fdx)
|
||||
op(GE,I,ilh)
|
||||
op(GE,U,ilhp)
|
||||
gop(GT,32)
|
||||
op(GT,F,fdx)
|
||||
op(GT,I,ilh)
|
||||
op(GT,U,ilhp)
|
||||
gop(LE,33)
|
||||
op(LE,F,fdx)
|
||||
op(LE,I,ilh)
|
||||
op(LE,U,ilhp)
|
||||
gop(LT,34)
|
||||
op(LT,F,fdx)
|
||||
op(LT,I,ilh)
|
||||
op(LT,U,ilhp)
|
||||
gop(NE,35)
|
||||
op(NE,F,fdx)
|
||||
op(NE,I,ilh)
|
||||
op(NE,U,ilhp)
|
||||
gop(JUMP,36)
|
||||
op(JUMP,V,-)
|
||||
gop(LABEL,37)
|
||||
op(LABEL,V,-)
|
||||
gop(LOAD,14)
|
||||
op(LOAD,B,-)
|
||||
op(LOAD,F,fdx)
|
||||
op(LOAD,I,csilh)
|
||||
op(LOAD,P,p)
|
||||
op(LOAD,U,csilhp)
|
||||
133
src/cmd/lccom-1/output.c
Normal file
133
src/cmd/lccom-1/output.c
Normal file
@@ -0,0 +1,133 @@
|
||||
#include "c.h"
|
||||
|
||||
static char *outs(const char *str, FILE *f, char *bp) {
|
||||
if (f)
|
||||
fputs(str, f);
|
||||
else
|
||||
while ((*bp = *str++))
|
||||
bp++;
|
||||
return bp;
|
||||
}
|
||||
|
||||
static char *outd(long n, FILE *f, char *bp) {
|
||||
unsigned long m;
|
||||
char buf[25], *s = buf + sizeof buf;
|
||||
|
||||
*--s = '\0';
|
||||
if (n < 0)
|
||||
m = -n;
|
||||
else
|
||||
m = n;
|
||||
do
|
||||
*--s = m%10 + '0';
|
||||
while ((m /= 10) != 0);
|
||||
if (n < 0)
|
||||
*--s = '-';
|
||||
return outs(s, f, bp);
|
||||
}
|
||||
|
||||
static char *outu(unsigned long n, int base, FILE *f, char *bp) {
|
||||
char buf[25], *s = buf + sizeof buf;
|
||||
|
||||
*--s = '\0';
|
||||
do
|
||||
*--s = "0123456789abcdef"[n%base];
|
||||
while ((n /= base) != 0);
|
||||
return outs(s, f, bp);
|
||||
}
|
||||
void print(const char *fmt, ...) {
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vfprint(stdout, NULL, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
/* fprint - formatted output to f */
|
||||
void fprint(FILE *f, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vfprint(f, NULL, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/* stringf - formatted output to a saved string */
|
||||
char *stringf(const char *fmt, ...) {
|
||||
char buf[1024];
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vfprint(NULL, buf, fmt, ap);
|
||||
va_end(ap);
|
||||
return string(buf);
|
||||
}
|
||||
|
||||
/* vfprint - formatted output to f or string bp */
|
||||
void vfprint(FILE *f, char *bp, const char *fmt, va_list ap) {
|
||||
for (; *fmt; fmt++)
|
||||
if (*fmt == '%')
|
||||
switch (*++fmt) {
|
||||
case 'd': bp = outd(va_arg(ap, int), f, bp); break;
|
||||
case 'D': bp = outd(va_arg(ap, long), f, bp); break;
|
||||
case 'U': bp = outu(va_arg(ap, unsigned long), 10, f, bp); break;
|
||||
case 'u': bp = outu(va_arg(ap, unsigned), 10, f, bp); break;
|
||||
case 'o': bp = outu(va_arg(ap, unsigned), 8, f, bp); break;
|
||||
case 'X': bp = outu(va_arg(ap, unsigned long), 16, f, bp); break;
|
||||
case 'x': bp = outu(va_arg(ap, unsigned), 16, f, bp); break;
|
||||
case 'f': case 'e':
|
||||
case 'g': {
|
||||
static char format[] = "%f";
|
||||
char buf[128];
|
||||
format[1] = *fmt;
|
||||
sprintf(buf, format, va_arg(ap, double));
|
||||
bp = outs(buf, f, bp);
|
||||
}
|
||||
; break;
|
||||
case 's': bp = outs(va_arg(ap, char *), f, bp); break;
|
||||
case 'p': {
|
||||
void *p = va_arg(ap, void *);
|
||||
if (p)
|
||||
bp = outs("0x", f, bp);
|
||||
bp = outu((unsigned long)p, 16, f, bp);
|
||||
break;
|
||||
}
|
||||
case 'c': if (f) fputc(va_arg(ap, int), f); else *bp++ = va_arg(ap, int); break;
|
||||
case 'S': { char *s = va_arg(ap, char *);
|
||||
int n = va_arg(ap, int);
|
||||
if (s)
|
||||
for ( ; n-- > 0; s++)
|
||||
if (f) (void)putc(*s, f); else *bp++ = *s;
|
||||
} break;
|
||||
case 'k': { int t = va_arg(ap, int);
|
||||
static char *tokens[] = {
|
||||
#define xx(a,b,c,d,e,f,g) g,
|
||||
#define yy(a,b,c,d,e,f,g) g,
|
||||
#include "token.h"
|
||||
};
|
||||
assert(tokens[t&0177]);
|
||||
bp = outs(tokens[t&0177], f, bp);
|
||||
} break;
|
||||
case 't': { Type ty = va_arg(ap, Type);
|
||||
assert(f);
|
||||
outtype(ty ? ty : voidtype, f);
|
||||
} break;
|
||||
case 'w': { Coordinate *p = va_arg(ap, Coordinate *);
|
||||
if (p->file && *p->file) {
|
||||
bp = outs(p->file, f, bp);
|
||||
bp = outs(":", f, bp);
|
||||
}
|
||||
bp = outd(p->y, f, bp);
|
||||
} break;
|
||||
case 'I': { int n = va_arg(ap, int);
|
||||
while (--n >= 0)
|
||||
if (f) (void)putc(' ', f); else *bp++ = ' ';
|
||||
} break;
|
||||
default: if (f) (void)putc(*fmt, f); else *bp++ = *fmt; break;
|
||||
}
|
||||
else if (f)
|
||||
(void)putc(*fmt, f);
|
||||
else
|
||||
*bp++ = *fmt;
|
||||
if (!f)
|
||||
*bp = '\0';
|
||||
}
|
||||
231
src/cmd/lccom-1/prof.c
Normal file
231
src/cmd/lccom-1/prof.c
Normal file
@@ -0,0 +1,231 @@
|
||||
#include "c.h"
|
||||
|
||||
struct callsite {
|
||||
char *file, *name;
|
||||
union coordinate {
|
||||
unsigned int coord;
|
||||
struct { unsigned int y:16,x:10,index:6; } le;
|
||||
struct { unsigned int index:6,x:10,y:16; } be;
|
||||
} u;
|
||||
};
|
||||
struct func {
|
||||
struct func *link;
|
||||
struct caller *callers;
|
||||
char *name;
|
||||
union coordinate src;
|
||||
};
|
||||
struct map { /* source code map; 200 coordinates/map */
|
||||
int size;
|
||||
union coordinate u[200];
|
||||
};
|
||||
|
||||
int npoints; /* # of execution points if -b specified */
|
||||
int ncalled = -1; /* #times prof.out says current function was called */
|
||||
static Symbol YYlink; /* symbol for file's struct _bbdata */
|
||||
static Symbol YYcounts; /* symbol for _YYcounts if -b specified */
|
||||
static List maplist; /* list of struct map *'s */
|
||||
static List filelist; /* list of file names */
|
||||
static Symbol funclist; /* list of struct func *'s */
|
||||
static Symbol afunc; /* current function's struct func */
|
||||
|
||||
/* bbpad - emit space, if necessary, to make size%align == 0; return new size */
|
||||
static int bbpad(int size, int align) {
|
||||
if (size%align) {
|
||||
(*IR->space)(align - size%align);
|
||||
size = roundup(size, align);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/* bbcall - build tree to set _callsite at call site *cp, emit call site data */
|
||||
static void bbcall(Symbol yycounts, Coordinate *cp, Tree *e) {
|
||||
static Symbol caller;
|
||||
Value v;
|
||||
union coordinate u;
|
||||
Symbol p = genident(STATIC, array(voidptype, 0, 0), GLOBAL);
|
||||
Tree t;
|
||||
|
||||
defglobal(p, LIT);
|
||||
defpointer(cp->file ? mkstr(cp->file)->u.c.loc : (Symbol)0);
|
||||
defpointer(mkstr(cfunc->name)->u.c.loc);
|
||||
if (IR->little_endian) {
|
||||
u.le.x = cp->x;
|
||||
u.le.y = cp->y;
|
||||
} else {
|
||||
u.be.x = cp->x;
|
||||
u.be.y = cp->y;
|
||||
}
|
||||
(*IR->defconst)(U, unsignedtype->size, (v.u = u.coord, v));
|
||||
bbpad(2*voidptype->size + unsignedtype->size, p->type->align);
|
||||
if (caller == 0) {
|
||||
caller = mksymbol(EXTERN, "_caller", ptr(voidptype));
|
||||
caller->defined = 0;
|
||||
}
|
||||
for (t = *e; generic(t->op) != CALL; t = t->kids[0])
|
||||
assert(t->op == RIGHT || !t->kids[1]);
|
||||
assert(generic(t->op) == CALL);
|
||||
t = tree(t->op, t->type,
|
||||
tree(RIGHT, t->kids[0]->type,
|
||||
t->kids[0],
|
||||
tree(RIGHT, t->kids[0]->type, asgn(caller, idtree(p)), t->kids[0])),
|
||||
t->kids[1]);
|
||||
for ( ; generic((*e)->op) != CALL; e = &(*e)->kids[0])
|
||||
;
|
||||
*e = t;
|
||||
}
|
||||
|
||||
/* bbentry - return tree for _prologue(&afunc, &YYlink)' */
|
||||
static void bbentry(Symbol yylink, Symbol f, void *ignore) {
|
||||
static Symbol prologue;
|
||||
|
||||
afunc = genident(STATIC, array(voidptype, 4, 0), GLOBAL);
|
||||
if (prologue == 0) {
|
||||
prologue = mksymbol(EXTERN, "_prologue", ftype(inttype, voidptype, voidptype, NULL));
|
||||
prologue->defined = 0;
|
||||
}
|
||||
walk(vcall(prologue, voidtype, pointer(idtree(afunc)), pointer(idtree(yylink)), NULL), 0, 0);
|
||||
}
|
||||
|
||||
/* bbexit - return tree for _epilogue(&afunc)' */
|
||||
static void bbexit(Symbol yylink, Symbol f, Tree e) {
|
||||
static Symbol epilogue;
|
||||
|
||||
if (epilogue == 0) {
|
||||
epilogue = mksymbol(EXTERN, "_epilogue", ftype(inttype, voidptype, NULL));
|
||||
epilogue->defined = 0;
|
||||
}
|
||||
walk(vcall(epilogue, voidtype, pointer(idtree(afunc)), NULL), 0, 0);
|
||||
}
|
||||
|
||||
/* bbfile - add file to list of file names, return its index */
|
||||
static int bbfile(char *file) {
|
||||
if (file) {
|
||||
List lp;
|
||||
int i = 1;
|
||||
if ((lp = filelist) != NULL)
|
||||
do {
|
||||
lp = lp->link;
|
||||
if (((Symbol)lp->x)->u.c.v.p == file)
|
||||
return i;
|
||||
i++;
|
||||
} while (lp != filelist);
|
||||
filelist = append(mkstr(file), filelist);
|
||||
return i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* bbfunc - emit function name and src coordinates */
|
||||
static void bbfunc(Symbol yylink, Symbol f, void *ignore) {
|
||||
Value v;
|
||||
union coordinate u;
|
||||
|
||||
defglobal(afunc, DATA);
|
||||
defpointer(funclist);
|
||||
defpointer(NULL);
|
||||
defpointer(mkstr(f->name)->u.c.loc);
|
||||
if (IR->little_endian) {
|
||||
u.le.x = f->u.f.pt.x;
|
||||
u.le.y = f->u.f.pt.y;
|
||||
u.le.index = bbfile(f->u.f.pt.file);
|
||||
} else {
|
||||
u.be.x = f->u.f.pt.x;
|
||||
u.be.y = f->u.f.pt.y;
|
||||
u.be.index = bbfile(f->u.f.pt.file);
|
||||
}
|
||||
(*IR->defconst)(U, unsignedtype->size, (v.u = u.coord, v));
|
||||
bbpad(3*voidptype->size + unsignedtype->size, afunc->type->align);
|
||||
funclist = afunc;
|
||||
}
|
||||
|
||||
/* bbincr - build tree to increment execution point at *cp */
|
||||
static void bbincr(Symbol yycounts, Coordinate *cp, Tree *e) {
|
||||
struct map *mp = maplist->x;
|
||||
Tree t;
|
||||
|
||||
if (needconst)
|
||||
return;
|
||||
/* append *cp to source map */
|
||||
if (mp->size >= NELEMS(mp->u)) {
|
||||
NEW(mp, PERM);
|
||||
mp->size = 0;
|
||||
maplist = append(mp, maplist);
|
||||
}
|
||||
if (IR->little_endian) {
|
||||
mp->u[mp->size].le.x = cp->x;
|
||||
mp->u[mp->size].le.y = cp->y;
|
||||
mp->u[mp->size++].le.index = bbfile(cp->file);
|
||||
} else {
|
||||
mp->u[mp->size].be.x = cp->x;
|
||||
mp->u[mp->size].be.y = cp->y;
|
||||
mp->u[mp->size++].be.index = bbfile(cp->file);
|
||||
}
|
||||
t = incr('+', rvalue((*optree['+'])(ADD, pointer(idtree(yycounts)),
|
||||
consttree(npoints++, inttype))), consttree(1, inttype));
|
||||
if (*e)
|
||||
*e = tree(RIGHT, (*e)->type, t, *e);
|
||||
else
|
||||
*e = t;
|
||||
}
|
||||
|
||||
/* bbvars - emit definition for basic block counting data */
|
||||
static void bbvars(Symbol yylink, void *ignore, void *ignore2) {
|
||||
int i, j, n = npoints;
|
||||
Value v;
|
||||
struct map **mp;
|
||||
Symbol coords, files, *p;
|
||||
|
||||
if (!YYcounts && !yylink)
|
||||
return;
|
||||
if (YYcounts) {
|
||||
if (n <= 0)
|
||||
n = 1;
|
||||
YYcounts->type = array(inttype, n, 0);
|
||||
defglobal(YYcounts, BSS);
|
||||
(*IR->space)(YYcounts->type->size);
|
||||
}
|
||||
files = genident(STATIC, array(charptype, 1, 0), GLOBAL);
|
||||
defglobal(files, LIT);
|
||||
for (p = ltov(&filelist, PERM); *p; p++)
|
||||
defpointer((*p)->u.c.loc);
|
||||
defpointer(NULL);
|
||||
coords = genident(STATIC, array(unsignedtype, n, 0), GLOBAL);
|
||||
defglobal(coords, LIT);
|
||||
for (i = n, mp = ltov(&maplist, PERM); *mp; i -= (*mp)->size, mp++)
|
||||
for (j = 0; j < (*mp)->size; j++)
|
||||
(*IR->defconst)(U, unsignedtype->size, (v.u = (*mp)->u[j].coord, v));
|
||||
if (i > 0)
|
||||
(*IR->space)(i*coords->type->type->size);
|
||||
(*IR->defconst)(U, unsignedtype->size, (v.u = 0, v));
|
||||
defglobal(yylink, DATA);
|
||||
defpointer(NULL);
|
||||
(*IR->defconst)(U, inttype->size, (v.u = n, v));
|
||||
bbpad(voidptype->size + inttype->size, yylink->type->align);
|
||||
defpointer(YYcounts);
|
||||
defpointer(coords);
|
||||
defpointer(files);
|
||||
defpointer(funclist);
|
||||
}
|
||||
|
||||
/* profInit - initialize basic block profiling options */
|
||||
void profInit(char *arg) {
|
||||
if (strncmp(arg, "-a", 2) == 0) {
|
||||
if (ncalled == -1
|
||||
&& process(arg[2] ? &arg[2] : "prof.out") > 0)
|
||||
ncalled = 0;
|
||||
} else if ((strcmp(arg, "-b") == 0
|
||||
|| strcmp(arg, "-C") == 0) && YYlink == 0) {
|
||||
YYlink = genident(STATIC, array(voidptype, 0, 0), GLOBAL);
|
||||
attach((Apply)bbentry, YYlink, &events.entry);
|
||||
attach((Apply)bbexit, YYlink, &events.returns);
|
||||
attach((Apply)bbfunc, YYlink, &events.exit);
|
||||
attach((Apply)bbvars, YYlink, &events.end);
|
||||
if (strcmp(arg, "-b") == 0) {
|
||||
YYcounts = genident(STATIC, array(inttype, 0, 0), GLOBAL);
|
||||
maplist = append(allocate(sizeof (struct map), PERM), maplist);
|
||||
((struct map *)maplist->x)->size = 0;
|
||||
attach((Apply)bbcall, YYcounts, &events.calls);
|
||||
attach((Apply)bbincr, YYcounts, &events.points);
|
||||
}
|
||||
}
|
||||
}
|
||||
285
src/cmd/lccom-1/profio.c
Normal file
285
src/cmd/lccom-1/profio.c
Normal file
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* C compiler: prof.out input
|
||||
*
|
||||
* prof.out format:
|
||||
* #files
|
||||
* name
|
||||
* ... (#files-1 times)
|
||||
* #functions
|
||||
* name file# x y count caller file x y
|
||||
* ... (#functions-1 times)
|
||||
* #points
|
||||
* file# x y count
|
||||
* ... (#points-1 times)
|
||||
*/
|
||||
#include "c.h"
|
||||
|
||||
struct count { /* count data: */
|
||||
int x, y; /* source coordinate */
|
||||
int count; /* associated execution count */
|
||||
};
|
||||
|
||||
#define MAXTOKEN 64
|
||||
|
||||
struct file { /* per-file prof.out data: */
|
||||
struct file *link; /* link to next file */
|
||||
char *name; /* file name */
|
||||
int size; /* size of counts[] */
|
||||
int count; /* counts[0..count-1] hold valid data */
|
||||
struct count *counts; /* count data */
|
||||
struct func { /* function data: */
|
||||
struct func *link; /* link to next function */
|
||||
char *name; /* function name */
|
||||
struct count count; /* total number of calls */
|
||||
struct caller { /* caller data: */
|
||||
struct caller *link; /* link to next caller */
|
||||
char *name; /* caller's name */
|
||||
char *file; /* call site: file, x, y */
|
||||
int x, y;
|
||||
int count; /* number of calls from this site */
|
||||
} *callers;
|
||||
} *funcs; /* list of functions */
|
||||
} *filelist;
|
||||
FILE *fp;
|
||||
|
||||
/* acaller - add caller and site (file,x,y) to callee's callers list */
|
||||
static void acaller(char *caller, char *file, int x, int y, int count, struct func *callee) {
|
||||
struct caller *q;
|
||||
|
||||
assert(callee);
|
||||
for (q = callee->callers; q && (caller != q->name
|
||||
|| file != q->file || x != q->x || y != q->y); q = q->link)
|
||||
;
|
||||
if (!q) {
|
||||
struct caller **r;
|
||||
NEW(q, PERM);
|
||||
q->name = caller;
|
||||
q->file = file;
|
||||
q->x = x;
|
||||
q->y = y;
|
||||
q->count = 0;
|
||||
for (r = &callee->callers; *r && (strcmp(q->name, (*r)->name) > 0
|
||||
|| strcmp(q->file, (*r)->file) > 0 || q->y > (*r)->y || q->y > (*r)->y); r = &(*r)->link)
|
||||
;
|
||||
q->link = *r;
|
||||
*r = q;
|
||||
}
|
||||
q->count += count;
|
||||
}
|
||||
|
||||
/* compare - return <0, 0, >0 if a<b, a==b, a>b, resp. */
|
||||
static int compare(const void *x, const void *y) {
|
||||
struct count *a = (struct count *)x, *b = (struct count *)y;
|
||||
|
||||
if (a->y == b->y)
|
||||
return a->x - b->x;
|
||||
return a->y - b->y;
|
||||
}
|
||||
|
||||
/* findfile - return file name's file list entry, or 0 */
|
||||
static struct file *findfile(char *name) {
|
||||
struct file *p;
|
||||
|
||||
for (p = filelist; p; p = p->link)
|
||||
if (p->name == name)
|
||||
return p;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* afunction - add function name and its data to file's function list */
|
||||
static struct func *afunction(char *name, char *file, int x, int y, int count) {
|
||||
struct file *p = findfile(file);
|
||||
struct func *q;
|
||||
|
||||
assert(p);
|
||||
for (q = p->funcs; q && name != q->name; q = q->link)
|
||||
;
|
||||
if (!q) {
|
||||
struct func **r;
|
||||
NEW(q, PERM);
|
||||
q->name = name;
|
||||
q->count.x = x;
|
||||
q->count.y = y;
|
||||
q->count.count = 0;
|
||||
q->callers = 0;
|
||||
for (r = &p->funcs; *r && compare(&q->count, &(*r)->count) > 0; r = &(*r)->link)
|
||||
;
|
||||
q->link = *r;
|
||||
*r = q;
|
||||
}
|
||||
q->count.count += count;
|
||||
return q;
|
||||
}
|
||||
|
||||
/* apoint - append execution point i to file's data */
|
||||
static void apoint(int i, char *file, int x, int y, int count) {
|
||||
struct file *p = findfile(file);
|
||||
|
||||
assert(p);
|
||||
if (i >= p->size) {
|
||||
int j;
|
||||
if (p->size == 0) {
|
||||
p->size = i >= 200 ? 2*i : 200;
|
||||
p->counts = newarray(p->size, sizeof *p->counts, PERM);
|
||||
} else {
|
||||
struct count *new;
|
||||
p->size = 2*i;
|
||||
new = newarray(p->size, sizeof *new, PERM);
|
||||
for (j = 0; j < p->count; j++)
|
||||
new[j] = p->counts[j];
|
||||
p->counts = new;
|
||||
}
|
||||
for (j = p->count; j < p->size; j++) {
|
||||
static struct count z;
|
||||
p->counts[j] = z;
|
||||
}
|
||||
}
|
||||
if (p->counts[i].x != x || p->counts[i].y != y)
|
||||
for (i = 0; i < p->count; i++)
|
||||
if (p->counts[i].x == x && p->counts[i].y == y)
|
||||
break;
|
||||
if (i >= p->count)
|
||||
if (i >= p->size)
|
||||
apoint(i, file, x, y, count);
|
||||
else {
|
||||
p->count = i + 1;
|
||||
p->counts[i].x = x;
|
||||
p->counts[i].y = y;
|
||||
p->counts[i].count = count;
|
||||
}
|
||||
else
|
||||
p->counts[i].count += count;
|
||||
}
|
||||
|
||||
/* findcount - return count associated with (file,x,y) or -1 */
|
||||
int findcount(char *file, int x, int y) {
|
||||
static struct file *cursor;
|
||||
|
||||
if (cursor == 0 || cursor->name != file)
|
||||
cursor = findfile(file);
|
||||
if (cursor) {
|
||||
int l, u;
|
||||
struct count *c = cursor->counts;
|
||||
for (l = 0, u = cursor->count - 1; l <= u; ) {
|
||||
int k = (l + u)/2;
|
||||
if (c[k].y > y || (c[k].y == y && c[k].x > x))
|
||||
u = k - 1;
|
||||
else if (c[k].y < y || (c[k].y == y && c[k].x < x))
|
||||
l = k + 1;
|
||||
else
|
||||
return c[k].count;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* findfunc - return count associated with function name in file or -1 */
|
||||
int findfunc(char *name, char *file) {
|
||||
static struct file *cursor;
|
||||
|
||||
if (cursor == 0 || cursor->name != file)
|
||||
cursor = findfile(file);
|
||||
if (cursor) {
|
||||
struct func *p;
|
||||
for (p = cursor->funcs; p; p = p->link)
|
||||
if (p->name == name)
|
||||
return p->count.count;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* getd - read a nonnegative number */
|
||||
static int getd(void) {
|
||||
int c, n = 0;
|
||||
|
||||
while ((c = getc(fp)) != EOF && (c == ' ' || c == '\n' || c == '\t'))
|
||||
;
|
||||
if (c >= '0' && c <= '9') {
|
||||
do
|
||||
n = 10*n + (c - '0');
|
||||
while ((c = getc(fp)) >= '0' && c <= '9');
|
||||
return n;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* getstr - read a string */
|
||||
static char *getstr(void) {
|
||||
int c;
|
||||
char buf[MAXTOKEN], *s = buf;
|
||||
|
||||
while ((c = getc(fp)) != EOF && c != ' ' && c != '\n' && c != '\t')
|
||||
if (s - buf < (int)sizeof buf - 2)
|
||||
*s++ = c;
|
||||
*s = 0;
|
||||
return s == buf ? (char *)0 : string(buf);
|
||||
}
|
||||
|
||||
/* gather - read prof.out data from fd */
|
||||
static int gather(void) {
|
||||
int i, nfiles, nfuncs, npoints;
|
||||
char *files[64];
|
||||
|
||||
if ((nfiles = getd()) < 0)
|
||||
return 0;
|
||||
assert(nfiles < NELEMS(files));
|
||||
for (i = 0; i < nfiles; i++) {
|
||||
if ((files[i] = getstr()) == 0)
|
||||
return -1;
|
||||
if (!findfile(files[i])) {
|
||||
struct file *new;
|
||||
NEW(new, PERM);
|
||||
new->name = files[i];
|
||||
new->size = new->count = 0;
|
||||
new->counts = 0;
|
||||
new->funcs = 0;
|
||||
new->link = filelist;
|
||||
filelist = new;
|
||||
}
|
||||
}
|
||||
if ((nfuncs = getd()) < 0)
|
||||
return -1;
|
||||
for (i = 0; i < nfuncs; i++) {
|
||||
struct func *q;
|
||||
char *name, *file;
|
||||
int f, x, y, count;
|
||||
if ((name = getstr()) == 0 || (f = getd()) <= 0
|
||||
|| (x = getd()) < 0 || (y = getd()) < 0 || (count = getd()) < 0)
|
||||
return -1;
|
||||
q = afunction(name, files[f-1], x, y, count);
|
||||
if ((name = getstr()) == 0 || (file = getstr()) == 0
|
||||
|| (x = getd()) < 0 || (y = getd()) < 0)
|
||||
return -1;
|
||||
if (*name != '?')
|
||||
acaller(name, file, x, y, count, q);
|
||||
}
|
||||
if ((npoints = getd()) < 0)
|
||||
return -1;
|
||||
for (i = 0; i < npoints; i++) {
|
||||
int f, x, y, count;
|
||||
if ((f = getd()) < 0 || (x = getd()) < 0 || (y = getd()) < 0
|
||||
|| (count = getd()) < 0)
|
||||
return -1;
|
||||
if (f)
|
||||
apoint(i, files[f-1], x, y, count);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* process - read prof.out data from file */
|
||||
int process(char *file) {
|
||||
int more;
|
||||
|
||||
if ((fp = fopen(file, "r")) != NULL) {
|
||||
struct file *p;
|
||||
while ((more = gather()) > 0)
|
||||
;
|
||||
fclose(fp);
|
||||
if (more < 0)
|
||||
return more;
|
||||
for (p = filelist; p; p = p->link)
|
||||
qsort(p->counts, p->count, sizeof *p->counts, compare);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
614
src/cmd/lccom-1/simp.c
Normal file
614
src/cmd/lccom-1/simp.c
Normal file
@@ -0,0 +1,614 @@
|
||||
#include "c.h"
|
||||
#include <float.h>
|
||||
|
||||
#define foldcnst(TYPE,VAR,OP) \
|
||||
if (l->op == CNST+TYPE && r->op == CNST+TYPE) \
|
||||
return cnsttree(ty, l->u.v.VAR OP r->u.v.VAR)
|
||||
#define commute(L,R) \
|
||||
if (generic(R->op) == CNST && generic(L->op) != CNST) \
|
||||
do { Tree t = L; L = R; R = t; } while(0)
|
||||
#define xfoldcnst(TYPE,VAR,OP,FUNC)\
|
||||
if (l->op == CNST+TYPE && r->op == CNST+TYPE\
|
||||
&& FUNC(l->u.v.VAR,r->u.v.VAR,\
|
||||
ty->u.sym->u.limits.min.VAR,\
|
||||
ty->u.sym->u.limits.max.VAR, needconst)) \
|
||||
return cnsttree(ty, l->u.v.VAR OP r->u.v.VAR)
|
||||
#define xcvtcnst(FTYPE,SRC,DST,VAR,EXPR) \
|
||||
if (l->op == CNST+FTYPE) do {\
|
||||
if (!explicitCast\
|
||||
&& ((SRC) < DST->u.sym->u.limits.min.VAR || (SRC) > DST->u.sym->u.limits.max.VAR))\
|
||||
warning("overflow in converting constant expression from `%t' to `%t'\n", l->type, DST);\
|
||||
if (needconst\
|
||||
|| !((SRC) < DST->u.sym->u.limits.min.VAR || (SRC) > DST->u.sym->u.limits.max.VAR))\
|
||||
return cnsttree(ty, (EXPR)); } while(0)
|
||||
#define identity(X,Y,TYPE,VAR,VAL) \
|
||||
if (X->op == CNST+TYPE && X->u.v.VAR == VAL) return Y
|
||||
#define zerofield(OP,TYPE,VAR) \
|
||||
if (l->op == FIELD \
|
||||
&& r->op == CNST+TYPE && r->u.v.VAR == 0)\
|
||||
return eqtree(OP, bittree(BAND, l->kids[0],\
|
||||
cnsttree(unsignedtype, \
|
||||
(unsigned long)fieldmask(l->u.field)<<fieldright(l->u.field))), r)
|
||||
#define cfoldcnst(TYPE,VAR,OP) \
|
||||
if (l->op == CNST+TYPE && r->op == CNST+TYPE) \
|
||||
return cnsttree(inttype, (long)(l->u.v.VAR OP r->u.v.VAR))
|
||||
#define foldaddp(L,R,RTYPE,VAR) \
|
||||
if (L->op == CNST+P && R->op == CNST+RTYPE) { \
|
||||
Tree e = tree(CNST+P, ty, NULL, NULL);\
|
||||
e->u.v.p = (char *)L->u.v.p + R->u.v.VAR;\
|
||||
return e; }
|
||||
#define ufoldcnst(TYPE,EXP) if (l->op == CNST+TYPE) return EXP
|
||||
#define sfoldcnst(OP) \
|
||||
if (l->op == CNST+U && r->op == CNST+I \
|
||||
&& r->u.v.i >= 0 && r->u.v.i < 8*l->type->size) \
|
||||
return cnsttree(ty, (unsigned long)(l->u.v.u OP r->u.v.i))
|
||||
#define geu(L,R,V) \
|
||||
if (R->op == CNST+U && R->u.v.u == 0) do { \
|
||||
warning("result of unsigned comparison is constant\n"); \
|
||||
return tree(RIGHT, inttype, root(L), cnsttree(inttype, (long)(V))); } while(0)
|
||||
#define idempotent(OP) if (l->op == OP) return l->kids[0]
|
||||
|
||||
int needconst;
|
||||
int explicitCast;
|
||||
|
||||
static int addi(long x, long y, long min, long max, int needconst)
|
||||
{
|
||||
int cond = x == 0
|
||||
|| y == 0
|
||||
|| (x < 0 && y < 0 && x >= min - y)
|
||||
|| (x < 0 && y > 0)
|
||||
|| (x > 0 && y < 0)
|
||||
|| (x > 0 && y > 0 && x <= max - y);
|
||||
|
||||
if (!cond && needconst) {
|
||||
warning("overflow in constant expression\n");
|
||||
cond = 1;
|
||||
}
|
||||
return cond;
|
||||
|
||||
|
||||
}
|
||||
|
||||
static int addd(double x, double y, double min, double max, int needconst)
|
||||
{
|
||||
int cond = x == 0
|
||||
|| y == 0
|
||||
|| (x < 0 && y < 0 && x >= min - y)
|
||||
|| (x < 0 && y > 0)
|
||||
|| (x > 0 && y < 0)
|
||||
|| (x > 0 && y > 0 && x <= max - y);
|
||||
|
||||
if (!cond && needconst) {
|
||||
warning("overflow in constant expression\n");
|
||||
cond = 1;
|
||||
}
|
||||
return cond;
|
||||
|
||||
|
||||
}
|
||||
|
||||
static Tree addrtree(Tree e, long n, Type ty) {
|
||||
Symbol p = e->u.sym, q;
|
||||
|
||||
if (p->scope == GLOBAL
|
||||
|| p->sclass == STATIC || p->sclass == EXTERN)
|
||||
NEW0(q, PERM);
|
||||
else
|
||||
NEW0(q, FUNC);
|
||||
q->name = stringd(genlabel(1));
|
||||
q->sclass = p->sclass;
|
||||
q->scope = p->scope;
|
||||
assert(isptr(ty) || isarray(ty));
|
||||
q->type = isptr(ty) ? ty->type : ty;
|
||||
q->temporary = p->temporary;
|
||||
q->generated = p->generated;
|
||||
q->addressed = p->addressed;
|
||||
q->computed = 1;
|
||||
q->defined = 1;
|
||||
q->ref = 1;
|
||||
assert(IR->address);
|
||||
if (p->scope == GLOBAL
|
||||
|| p->sclass == STATIC || p->sclass == EXTERN) {
|
||||
if (p->sclass == AUTO)
|
||||
q->sclass = STATIC;
|
||||
(*IR->address)(q, p, n);
|
||||
} else {
|
||||
Code cp;
|
||||
addlocal(p);
|
||||
cp = code(Address);
|
||||
cp->u.addr.sym = q;
|
||||
cp->u.addr.base = p;
|
||||
cp->u.addr.offset = n;
|
||||
}
|
||||
e = tree(e->op, ty, NULL, NULL);
|
||||
e->u.sym = q;
|
||||
return e;
|
||||
}
|
||||
|
||||
/* div[id] - return 1 if min <= x/y <= max, 0 otherwise */
|
||||
static int divi(long x, long y, long min, long max, int needconst) {
|
||||
int cond = y != 0 && !(x == min && y == -1);
|
||||
if (!cond && needconst) {
|
||||
warning("overflow in constant expression\n");
|
||||
cond = 1;
|
||||
}
|
||||
return cond;
|
||||
|
||||
|
||||
}
|
||||
|
||||
static int divd(double x, double y, double min, double max, int needconst) {
|
||||
int cond;
|
||||
|
||||
if (x < 0) x = -x;
|
||||
if (y < 0) y = -y;
|
||||
cond = y != 0 && !(y < 1 && x > max*y);
|
||||
if (!cond && needconst) {
|
||||
warning("overflow in constant expression\n");
|
||||
cond = 1;
|
||||
}
|
||||
return cond;
|
||||
|
||||
}
|
||||
|
||||
/* mul[id] - return 1 if min <= x*y <= max, 0 otherwise */
|
||||
static int muli(long x, long y, long min, long max, int needconst)
|
||||
{
|
||||
int cond = (x > -1 && x <= 1)
|
||||
|| (y > -1 && y <= 1)
|
||||
|| (x < 0 && y < 0 && -x <= max/-y)
|
||||
|| (x < 0 && y > 0 && x >= min/y)
|
||||
|| (x > 0 && y < 0 && y >= min/x)
|
||||
|| (x > 0 && y > 0 && x <= max/y);
|
||||
|
||||
if (! cond && needconst) {
|
||||
warning("overflow in constant expression\n");
|
||||
cond = 1;
|
||||
}
|
||||
return cond;
|
||||
}
|
||||
|
||||
static int muld(double x, double y, double min, double max, int needconst)
|
||||
{
|
||||
int cond = ((x >= -1 && x <= 1)
|
||||
|| (y >= -1 && y <= 1)
|
||||
|| (x < 0 && y < 0 && -x <= max/-y)
|
||||
|| (x < 0 && y > 0 && x >= min/y)
|
||||
|| (x > 0 && y < 0 && y >= min/x)
|
||||
|| (x > 0 && y > 0 && x <= max/y));
|
||||
|
||||
if (! cond && needconst) {
|
||||
warning("overflow in constant expression\n");
|
||||
cond = 1;
|
||||
}
|
||||
return cond;
|
||||
|
||||
|
||||
}
|
||||
/* sub[id] - return 1 if min <= x-y <= max, 0 otherwise */
|
||||
static int subi(long x, long y, long min, long max, int needconst) {
|
||||
return addi(x, -y, min, max, needconst);
|
||||
}
|
||||
|
||||
static int subd(double x, double y, double min, double max, int needconst) {
|
||||
return addd(x, -y, min, max, needconst);
|
||||
}
|
||||
Tree constexpr(int tok) {
|
||||
Tree p;
|
||||
|
||||
needconst++;
|
||||
p = expr1(tok);
|
||||
needconst--;
|
||||
return p;
|
||||
}
|
||||
|
||||
int intexpr(int tok, int n) {
|
||||
Tree p = constexpr(tok);
|
||||
|
||||
needconst++;
|
||||
if (p->op == CNST+I || p->op == CNST+U)
|
||||
n = cast(p, inttype)->u.v.i;
|
||||
else
|
||||
error("integer expression must be constant\n");
|
||||
needconst--;
|
||||
return n;
|
||||
}
|
||||
|
||||
Tree simplify(int op, Type ty, Tree l, Tree r)
|
||||
{
|
||||
int n;
|
||||
|
||||
if (optype(op) == 0)
|
||||
op = mkop(op, ty);
|
||||
switch (op) {
|
||||
case ADD+U:
|
||||
foldcnst(U,u,+);
|
||||
commute(r,l);
|
||||
identity(r,l,U,u,0);
|
||||
break;
|
||||
case ADD+I:
|
||||
xfoldcnst(I,i,+,addi);
|
||||
commute(r,l);
|
||||
identity(r,l,I,i,0);
|
||||
break;
|
||||
case CVI+I:
|
||||
xcvtcnst(I,l->u.v.i,ty,i,(long)extend(l->u.v.i,ty));
|
||||
break;
|
||||
case CVU+I:
|
||||
if (l->op == CNST+U) {
|
||||
if (!explicitCast && l->u.v.u > ty->u.sym->u.limits.max.i)
|
||||
warning("overflow in converting constant expression from `%t' to `%t'\n", l->type, ty);
|
||||
if (needconst || !(l->u.v.u > ty->u.sym->u.limits.max.i))
|
||||
return cnsttree(ty, (long)extend(l->u.v.u,ty));
|
||||
}
|
||||
break;
|
||||
case CVP+U:
|
||||
xcvtcnst(P,(unsigned long)l->u.v.p,ty,u,(unsigned long)l->u.v.p);
|
||||
break;
|
||||
case CVU+P:
|
||||
xcvtcnst(U,(void*)l->u.v.u,ty,p,(void*)l->u.v.u);
|
||||
break;
|
||||
case CVP+P:
|
||||
xcvtcnst(P,l->u.v.p,ty,p,l->u.v.p);
|
||||
break;
|
||||
case CVI+U:
|
||||
xcvtcnst(I,l->u.v.i,ty,u,((unsigned long)l->u.v.i)&ones(8*ty->size));
|
||||
break;
|
||||
case CVU+U:
|
||||
xcvtcnst(U,l->u.v.u,ty,u,l->u.v.u&ones(8*ty->size));
|
||||
break;
|
||||
|
||||
case CVI+F:
|
||||
xcvtcnst(I,l->u.v.i,ty,d,(long double)l->u.v.i);
|
||||
case CVU+F:
|
||||
xcvtcnst(U,l->u.v.u,ty,d,(long double)l->u.v.u);
|
||||
break;
|
||||
case CVF+I:
|
||||
xcvtcnst(F,l->u.v.d,ty,i,(long)l->u.v.d);
|
||||
break;
|
||||
case CVF+F: {
|
||||
float d;
|
||||
if (l->op == CNST+F) {
|
||||
if (l->u.v.d < ty->u.sym->u.limits.min.d)
|
||||
d = ty->u.sym->u.limits.min.d;
|
||||
else if (l->u.v.d > ty->u.sym->u.limits.max.d)
|
||||
d = ty->u.sym->u.limits.max.d;
|
||||
else
|
||||
d = l->u.v.d;
|
||||
}
|
||||
xcvtcnst(F,l->u.v.d,ty,d,(long double)d);
|
||||
break;
|
||||
}
|
||||
case BAND+U:
|
||||
foldcnst(U,u,&);
|
||||
commute(r,l);
|
||||
identity(r,l,U,u,ones(8*ty->size));
|
||||
if (r->op == CNST+U && r->u.v.u == 0)
|
||||
return tree(RIGHT, ty, root(l), cnsttree(ty, 0UL));
|
||||
break;
|
||||
case BAND+I:
|
||||
foldcnst(I,i,&);
|
||||
commute(r,l);
|
||||
identity(r,l,I,i,ones(8*ty->size));
|
||||
if (r->op == CNST+I && r->u.v.u == 0)
|
||||
return tree(RIGHT, ty, root(l), cnsttree(ty, 0L));
|
||||
break;
|
||||
|
||||
case MUL+U:
|
||||
commute(l,r);
|
||||
if (l->op == CNST+U && (n = ispow2(l->u.v.u)) != 0)
|
||||
return simplify(LSH, ty, r, cnsttree(inttype, (long)n));
|
||||
foldcnst(U,u,*);
|
||||
identity(r,l,U,u,1);
|
||||
break;
|
||||
case NE+I:
|
||||
cfoldcnst(I,i,!=);
|
||||
commute(r,l);
|
||||
zerofield(NE,I,i);
|
||||
break;
|
||||
|
||||
case EQ+I:
|
||||
cfoldcnst(I,i,==);
|
||||
commute(r,l);
|
||||
zerofield(EQ,I,i);
|
||||
break;
|
||||
case ADD+P:
|
||||
foldaddp(l,r,I,i);
|
||||
foldaddp(l,r,U,u);
|
||||
foldaddp(r,l,I,i);
|
||||
foldaddp(r,l,U,u);
|
||||
commute(r,l);
|
||||
identity(r,retype(l,ty),I,i,0);
|
||||
identity(r,retype(l,ty),U,u,0);
|
||||
/*
|
||||
Some assemblers, e.g., the MIPS, can't handle offsets
|
||||
larger than 16 bits. A better solution would be to change
|
||||
the interface so that address() could fail.
|
||||
*/
|
||||
if (l->op == ADDRG+P && l->u.sym->generated
|
||||
&& ((r->op == CNST+I && (r->u.v.i > 32767 || r->u.v.i < -32768))
|
||||
|| (r->op == CNST+U && r->u.v.u > 65536)))
|
||||
break;
|
||||
if (IR->address
|
||||
&& isaddrop(l->op)
|
||||
&& ((r->op == CNST+I && r->u.v.i <= longtype->u.sym->u.limits.max.i
|
||||
&& r->u.v.i >= longtype->u.sym->u.limits.min.i)
|
||||
|| (r->op == CNST+U && r->u.v.u <= longtype->u.sym->u.limits.max.i)))
|
||||
return addrtree(l, cast(r, longtype)->u.v.i, ty);
|
||||
if (IR->address
|
||||
&& l->op == ADD+P && isaddrop(l->kids[1]->op)
|
||||
&& ((r->op == CNST+I && r->u.v.i <= longtype->u.sym->u.limits.max.i
|
||||
&& r->u.v.i >= longtype->u.sym->u.limits.min.i)
|
||||
|| (r->op == CNST+U && r->u.v.u <= longtype->u.sym->u.limits.max.i)))
|
||||
return simplify(ADD+P, ty, l->kids[0],
|
||||
addrtree(l->kids[1], cast(r, longtype)->u.v.i, ty));
|
||||
if ((l->op == ADD+I || l->op == SUB+I)
|
||||
&& l->kids[1]->op == CNST+I && isaddrop(r->op))
|
||||
return simplify(ADD+P, ty, l->kids[0],
|
||||
simplify(generic(l->op)+P, ty, r, l->kids[1]));
|
||||
if (l->op == ADD+P && generic(l->kids[1]->op) == CNST
|
||||
&& generic(r->op) == CNST)
|
||||
return simplify(ADD+P, ty, l->kids[0],
|
||||
simplify(ADD, l->kids[1]->type, l->kids[1], r));
|
||||
if (l->op == ADD+I && generic(l->kids[1]->op) == CNST
|
||||
&& r->op == ADD+P && generic(r->kids[1]->op) == CNST)
|
||||
return simplify(ADD+P, ty, l->kids[0],
|
||||
simplify(ADD+P, ty, r->kids[0],
|
||||
simplify(ADD, r->kids[1]->type, l->kids[1], r->kids[1])));
|
||||
if (l->op == RIGHT && l->kids[1])
|
||||
return tree(RIGHT, ty, l->kids[0],
|
||||
simplify(ADD+P, ty, l->kids[1], r));
|
||||
else if (l->op == RIGHT && l->kids[0])
|
||||
return tree(RIGHT, ty,
|
||||
simplify(ADD+P, ty, l->kids[0], r), NULL);
|
||||
break;
|
||||
|
||||
case ADD+F:
|
||||
xfoldcnst(F,d,+,addd);
|
||||
commute(r,l);
|
||||
break;
|
||||
case AND+I:
|
||||
op = AND;
|
||||
ufoldcnst(I,l->u.v.i ? cond(r) : l); /* 0&&r => 0, 1&&r => r */
|
||||
break;
|
||||
case OR+I:
|
||||
op = OR;
|
||||
/* 0||r => r, 1||r => 1 */
|
||||
ufoldcnst(I,l->u.v.i ? cnsttree(ty, 1L) : cond(r));
|
||||
break;
|
||||
case BCOM+I:
|
||||
ufoldcnst(I,cnsttree(ty, (long)extend((~l->u.v.i)&ones(8*ty->size), ty)));
|
||||
idempotent(BCOM+U);
|
||||
break;
|
||||
case BCOM+U:
|
||||
ufoldcnst(U,cnsttree(ty, (unsigned long)((~l->u.v.u)&ones(8*ty->size))));
|
||||
idempotent(BCOM+U);
|
||||
break;
|
||||
case BOR+U:
|
||||
foldcnst(U,u,|);
|
||||
commute(r,l);
|
||||
identity(r,l,U,u,0);
|
||||
break;
|
||||
case BOR+I:
|
||||
foldcnst(I,i,|);
|
||||
commute(r,l);
|
||||
identity(r,l,I,i,0);
|
||||
break;
|
||||
case BXOR+U:
|
||||
foldcnst(U,u,^);
|
||||
commute(r,l);
|
||||
identity(r,l,U,u,0);
|
||||
break;
|
||||
case BXOR+I:
|
||||
foldcnst(I,i,^);
|
||||
commute(r,l);
|
||||
identity(r,l,I,i,0);
|
||||
break;
|
||||
case DIV+F:
|
||||
xfoldcnst(F,d,/,divd);
|
||||
break;
|
||||
case DIV+I:
|
||||
identity(r,l,I,i,1);
|
||||
if ((r->op == CNST+I && r->u.v.i == 0)
|
||||
|| (l->op == CNST+I && l->u.v.i == ty->u.sym->u.limits.min.i
|
||||
&& r->op == CNST+I && r->u.v.i == -1))
|
||||
break;
|
||||
xfoldcnst(I,i,/,divi);
|
||||
break;
|
||||
case DIV+U:
|
||||
identity(r,l,U,u,1);
|
||||
if (r->op == CNST+U && r->u.v.u == 0)
|
||||
break;
|
||||
if (r->op == CNST+U && (n = ispow2(r->u.v.u)) != 0)
|
||||
return simplify(RSH, ty, l, cnsttree(inttype, (long)n));
|
||||
foldcnst(U,u,/);
|
||||
break;
|
||||
case EQ+F:
|
||||
cfoldcnst(F,d,==);
|
||||
commute(r,l);
|
||||
break;
|
||||
case EQ+U:
|
||||
cfoldcnst(U,u,==);
|
||||
commute(r,l);
|
||||
zerofield(EQ,U,u);
|
||||
break;
|
||||
case GE+F: cfoldcnst(F,d,>=); break;
|
||||
case GE+I: cfoldcnst(I,i,>=); break;
|
||||
case GE+U:
|
||||
geu(l,r,1); /* l >= 0 => (l,1) */
|
||||
cfoldcnst(U,u,>=);
|
||||
if (l->op == CNST+U && l->u.v.u == 0) /* 0 >= r => r == 0 */
|
||||
return eqtree(EQ, r, l);
|
||||
break;
|
||||
case GT+F: cfoldcnst(F,d, >); break;
|
||||
case GT+I: cfoldcnst(I,i, >); break;
|
||||
case GT+U:
|
||||
geu(r,l,0); /* 0 > r => (r,0) */
|
||||
cfoldcnst(U,u, >);
|
||||
if (r->op == CNST+U && r->u.v.u == 0) /* l > 0 => l != 0 */
|
||||
return eqtree(NE, l, r);
|
||||
break;
|
||||
case LE+F: cfoldcnst(F,d,<=); break;
|
||||
case LE+I: cfoldcnst(I,i,<=); break;
|
||||
case LE+U:
|
||||
geu(r,l,1); /* 0 <= r => (r,1) */
|
||||
cfoldcnst(U,u,<=);
|
||||
if (r->op == CNST+U && r->u.v.u == 0) /* l <= 0 => l == 0 */
|
||||
return eqtree(EQ, l, r);
|
||||
break;
|
||||
case LSH+I:
|
||||
identity(r,l,I,i,0);
|
||||
if (l->op == CNST+I && r->op == CNST+I
|
||||
&& r->u.v.i >= 0 && r->u.v.i < 8*l->type->size
|
||||
&& muli(l->u.v.i, 1<<r->u.v.i, ty->u.sym->u.limits.min.i, ty->u.sym->u.limits.max.i, needconst))
|
||||
return cnsttree(ty, (long)(l->u.v.i<<r->u.v.i));
|
||||
if (r->op == CNST+I && (r->u.v.i >= 8*ty->size || r->u.v.i < 0)) {
|
||||
warning("shifting an `%t' by %d bits is undefined\n", ty, r->u.v.i);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case LSH+U:
|
||||
identity(r,l,I,i,0);
|
||||
sfoldcnst(<<);
|
||||
if (r->op == CNST+I && (r->u.v.i >= 8*ty->size || r->u.v.i < 0)) {
|
||||
warning("shifting an `%t' by %d bits is undefined\n", ty, r->u.v.i);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case LT+F: cfoldcnst(F,d, <); break;
|
||||
case LT+I: cfoldcnst(I,i, <); break;
|
||||
case LT+U:
|
||||
geu(l,r,0); /* l < 0 => (l,0) */
|
||||
cfoldcnst(U,u, <);
|
||||
if (l->op == CNST+U && l->u.v.u == 0) /* 0 < r => r != 0 */
|
||||
return eqtree(NE, r, l);
|
||||
break;
|
||||
case MOD+I:
|
||||
if ((r->op == CNST+I && r->u.v.i == 0)
|
||||
|| (l->op == CNST+I && l->u.v.i == ty->u.sym->u.limits.min.i
|
||||
&& r->op == CNST+I && r->u.v.i == -1))
|
||||
break;
|
||||
xfoldcnst(I,i,%,divi);
|
||||
if (r->op == CNST+I && r->u.v.i == 1) /* l%1 => (l,0) */
|
||||
return tree(RIGHT, ty, root(l), cnsttree(ty, 0L));
|
||||
break;
|
||||
case MOD+U:
|
||||
if (r->op == CNST+U && ispow2(r->u.v.u)) /* l%2^n => l&(2^n-1) */
|
||||
return bittree(BAND, l, cnsttree(ty, r->u.v.u - 1));
|
||||
if (r->op == CNST+U && r->u.v.u == 0)
|
||||
break;
|
||||
foldcnst(U,u,%);
|
||||
break;
|
||||
case MUL+F:
|
||||
xfoldcnst(F,d,*,muld);
|
||||
commute(l,r);
|
||||
break;
|
||||
case MUL+I:
|
||||
commute(l,r);
|
||||
xfoldcnst(I,i,*,muli);
|
||||
if (l->op == CNST+I && r->op == ADD+I && r->kids[1]->op == CNST+I)
|
||||
/* c1*(x + c2) => c1*x + c1*c2 */
|
||||
return simplify(ADD, ty, simplify(MUL, ty, l, r->kids[0]),
|
||||
simplify(MUL, ty, l, r->kids[1]));
|
||||
if (l->op == CNST+I && r->op == SUB+I && r->kids[1]->op == CNST+I)
|
||||
/* c1*(x - c2) => c1*x - c1*c2 */
|
||||
return simplify(SUB, ty, simplify(MUL, ty, l, r->kids[0]),
|
||||
simplify(MUL, ty, l, r->kids[1]));
|
||||
if (l->op == CNST+I && l->u.v.i > 0 && (n = ispow2(l->u.v.i)) != 0)
|
||||
/* 2^n * r => r<<n */
|
||||
return simplify(LSH, ty, r, cnsttree(inttype, (long)n));
|
||||
identity(r,l,I,i,1);
|
||||
break;
|
||||
case NE+F:
|
||||
cfoldcnst(F,d,!=);
|
||||
commute(r,l);
|
||||
break;
|
||||
case NE+U:
|
||||
cfoldcnst(U,u,!=);
|
||||
commute(r,l);
|
||||
zerofield(NE,U,u);
|
||||
break;
|
||||
case NEG+F:
|
||||
ufoldcnst(F,cnsttree(ty, -l->u.v.d));
|
||||
idempotent(NEG+F);
|
||||
break;
|
||||
case NEG+I:
|
||||
if (l->op == CNST+I) {
|
||||
if (needconst && l->u.v.i == ty->u.sym->u.limits.min.i)
|
||||
warning("overflow in constant expression\n");
|
||||
if (needconst || l->u.v.i != ty->u.sym->u.limits.min.i)
|
||||
return cnsttree(ty, -l->u.v.i);
|
||||
}
|
||||
idempotent(NEG+I);
|
||||
break;
|
||||
case NOT+I:
|
||||
op = NOT;
|
||||
ufoldcnst(I,cnsttree(ty, !l->u.v.i));
|
||||
break;
|
||||
case RSH+I:
|
||||
identity(r,l,I,i,0);
|
||||
if (l->op == CNST+I && r->op == CNST+I
|
||||
&& r->u.v.i >= 0 && r->u.v.i < 8*l->type->size) {
|
||||
long n = l->u.v.i>>r->u.v.i;
|
||||
if (l->u.v.i < 0)
|
||||
n |= ~0UL<<(8*l->type->size - r->u.v.i);
|
||||
return cnsttree(ty, n);
|
||||
}
|
||||
if (r->op == CNST+I && (r->u.v.i >= 8*ty->size || r->u.v.i < 0)) {
|
||||
warning("shifting an `%t' by %d bits is undefined\n", ty, r->u.v.i);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case RSH+U:
|
||||
identity(r,l,I,i,0);
|
||||
sfoldcnst(>>);
|
||||
if (r->op == CNST+I && (r->u.v.i >= 8*ty->size || r->u.v.i < 0)) {
|
||||
warning("shifting an `%t' by %d bits is undefined\n", ty, r->u.v.i);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case SUB+F:
|
||||
xfoldcnst(F,d,-,subd);
|
||||
break;
|
||||
case SUB+I:
|
||||
xfoldcnst(I,i,-,subi);
|
||||
identity(r,l,I,i,0);
|
||||
break;
|
||||
case SUB+U:
|
||||
foldcnst(U,u,-);
|
||||
identity(r,l,U,u,0);
|
||||
break;
|
||||
case SUB+P:
|
||||
if (l->op == CNST+P && r->op == CNST+P)
|
||||
return cnsttree(ty, (long)((char *)l->u.v.p - (char *)r->u.v.p));
|
||||
if (r->op == CNST+I || r->op == CNST+U)
|
||||
return simplify(ADD, ty, l,
|
||||
cnsttree(inttype, r->op == CNST+I ? -r->u.v.i : -(long)r->u.v.u));
|
||||
if (isaddrop(l->op) && r->op == ADD+I && r->kids[1]->op == CNST+I)
|
||||
/* l - (x + c) => l-c - x */
|
||||
return simplify(SUB, ty,
|
||||
simplify(SUB, ty, l, r->kids[1]), r->kids[0]);
|
||||
break;
|
||||
default:assert(0);
|
||||
}
|
||||
return tree(op, ty, l, r);
|
||||
}
|
||||
|
||||
/*
|
||||
* ispow2 - if u > 1 && u == 2^n, return n, otherwise return 0
|
||||
*/
|
||||
int ispow2(unsigned long u)
|
||||
{
|
||||
int n;
|
||||
|
||||
if (u > 1 && (u&(u-1)) == 0)
|
||||
for (n = 0; u; u >>= 1, n++)
|
||||
if (u&1)
|
||||
return n;
|
||||
return 0;
|
||||
}
|
||||
1176
src/cmd/lccom-1/sparc.md
Normal file
1176
src/cmd/lccom-1/sparc.md
Normal file
File diff suppressed because it is too large
Load Diff
329
src/cmd/lccom-1/stab.c
Normal file
329
src/cmd/lccom-1/stab.c
Normal file
@@ -0,0 +1,329 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "c.h"
|
||||
#include "stab.h"
|
||||
|
||||
static char *currentfile; /* current file name */
|
||||
static int ntypes;
|
||||
|
||||
extern Interface sparcIR;
|
||||
|
||||
char *stabprefix = "L";
|
||||
|
||||
extern char *stabprefix;
|
||||
extern void stabblock(int, int, Symbol*);
|
||||
extern void stabend(Coordinate *, Symbol, Coordinate **, Symbol *, Symbol *);
|
||||
extern void stabfend(Symbol, int);
|
||||
extern void stabinit(char *, int, char *[]);
|
||||
extern void stabline(Coordinate *);
|
||||
extern void stabsym(Symbol);
|
||||
extern void stabtype(Symbol);
|
||||
|
||||
static void asgncode(Type, int);
|
||||
static void dbxout(Type);
|
||||
static int dbxtype(Type);
|
||||
static int emittype(Type, int, int);
|
||||
|
||||
/* asgncode - assign type code to ty */
|
||||
static void asgncode(Type ty, int lev) {
|
||||
if (ty->x.marked || ty->x.typeno)
|
||||
return;
|
||||
ty->x.marked = 1;
|
||||
switch (ty->op) {
|
||||
case VOLATILE: case CONST: case VOLATILE+CONST:
|
||||
asgncode(ty->type, lev);
|
||||
ty->x.typeno = ty->type->x.typeno;
|
||||
break;
|
||||
case POINTER: case FUNCTION: case ARRAY:
|
||||
asgncode(ty->type, lev + 1);
|
||||
/* fall thru */
|
||||
case VOID: case INT: case UNSIGNED: case FLOAT:
|
||||
break;
|
||||
case STRUCT: case UNION: {
|
||||
Field p;
|
||||
for (p = fieldlist(ty); p; p = p->link)
|
||||
asgncode(p->type, lev + 1);
|
||||
/* fall thru */
|
||||
case ENUM:
|
||||
if (ty->x.typeno == 0)
|
||||
ty->x.typeno = ++ntypes;
|
||||
if (lev > 0 && (*ty->u.sym->name < '0' || *ty->u.sym->name > '9'))
|
||||
dbxout(ty);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* dbxout - output .stabs entry for type ty */
|
||||
static void dbxout(Type ty) {
|
||||
ty = unqual(ty);
|
||||
if (!ty->x.printed) {
|
||||
int col = 0;
|
||||
print(".stabs \""), col += 8;
|
||||
if (ty->u.sym && !(isfunc(ty) || isarray(ty) || isptr(ty)))
|
||||
print("%s", ty->u.sym->name), col += strlen(ty->u.sym->name);
|
||||
print(":%c", isstruct(ty) || isenum(ty) ? 'T' : 't'), col += 2;
|
||||
emittype(ty, 0, col);
|
||||
print("\",%d,0,0,0\n", N_LSYM);
|
||||
}
|
||||
}
|
||||
|
||||
/* dbxtype - emit a stabs entry for type ty, return type code */
|
||||
static int dbxtype(Type ty) {
|
||||
asgncode(ty, 0);
|
||||
dbxout(ty);
|
||||
return ty->x.typeno;
|
||||
}
|
||||
|
||||
/*
|
||||
* emittype - emit ty's type number, emitting its definition if necessary.
|
||||
* Returns the output column number after emission; col is the approximate
|
||||
* output column before emission and is used to emit continuation lines for long
|
||||
* struct, union, and enum types. Continuations are not emitted for other types,
|
||||
* even if the definition is long. lev is the depth of calls to emittype.
|
||||
*/
|
||||
static int emittype(Type ty, int lev, int col) {
|
||||
int tc = ty->x.typeno;
|
||||
|
||||
if (isconst(ty) || isvolatile(ty)) {
|
||||
col = emittype(ty->type, lev, col);
|
||||
ty->x.typeno = ty->type->x.typeno;
|
||||
ty->x.printed = 1;
|
||||
return col;
|
||||
}
|
||||
if (tc == 0) {
|
||||
ty->x.typeno = tc = ++ntypes;
|
||||
/* fprint(2,"`%t'=%d\n", ty, tc); */
|
||||
}
|
||||
print("%d", tc), col += 3;
|
||||
if (ty->x.printed)
|
||||
return col;
|
||||
ty->x.printed = 1;
|
||||
switch (ty->op) {
|
||||
case VOID: /* void is defined as itself */
|
||||
print("=%d", tc), col += 1+3;
|
||||
break;
|
||||
case INT:
|
||||
if (ty == chartype) /* plain char is a subrange of itself */
|
||||
print("=r%d;%d;%d;", tc, ty->u.sym->u.limits.min.i, ty->u.sym->u.limits.max.i),
|
||||
col += 2+3+2*2.408*ty->size+2;
|
||||
else /* other signed ints are subranges of int */
|
||||
print("=r1;%D;%D;", ty->u.sym->u.limits.min.i, ty->u.sym->u.limits.max.i),
|
||||
col += 4+2*2.408*ty->size+2;
|
||||
break;
|
||||
case UNSIGNED:
|
||||
if (ty == chartype) /* plain char is a subrange of itself */
|
||||
print("=r%d;0;%u;", tc, ty->u.sym->u.limits.max.i),
|
||||
col += 2+3+2+2.408*ty->size+1;
|
||||
else /* other signed ints are subranges of int */
|
||||
print("=r1;0;%U;", ty->u.sym->u.limits.max.i),
|
||||
col += 4+2.408*ty->size+1;
|
||||
break;
|
||||
case FLOAT: /* float, double, long double get sizes, not ranges */
|
||||
print("=r1;%d;0;", ty->size), col += 4+1+3;
|
||||
break;
|
||||
case POINTER:
|
||||
print("=*"), col += 2;
|
||||
col = emittype(ty->type, lev + 1, col);
|
||||
break;
|
||||
case FUNCTION:
|
||||
print("=f"), col += 2;
|
||||
col = emittype(ty->type, lev + 1, col);
|
||||
break;
|
||||
case ARRAY: /* array includes subscript as an int range */
|
||||
if (ty->size && ty->type->size)
|
||||
print("=ar1;0;%d;", ty->size/ty->type->size - 1), col += 7+3+1;
|
||||
else
|
||||
print("=ar1;0;-1;"), col += 10;
|
||||
col = emittype(ty->type, lev + 1, col);
|
||||
break;
|
||||
case STRUCT: case UNION: {
|
||||
Field p;
|
||||
if (!ty->u.sym->defined) {
|
||||
print("=x%c%s:", ty->op == STRUCT ? 's' : 'u', ty->u.sym->name);
|
||||
col += 2+1+strlen(ty->u.sym->name)+1;
|
||||
break;
|
||||
}
|
||||
if (lev > 0 && (*ty->u.sym->name < '0' || *ty->u.sym->name > '9')) {
|
||||
ty->x.printed = 0;
|
||||
break;
|
||||
}
|
||||
print("=%c%d", ty->op == STRUCT ? 's' : 'u', ty->size), col += 1+1+3;
|
||||
for (p = fieldlist(ty); p; p = p->link) {
|
||||
if (p->name)
|
||||
print("%s:", p->name), col += strlen(p->name)+1;
|
||||
else
|
||||
print(":"), col += 1;
|
||||
col = emittype(p->type, lev + 1, col);
|
||||
if (p->lsb)
|
||||
print(",%d,%d;", 8*p->offset +
|
||||
(IR->little_endian ? fieldright(p) : fieldleft(p)),
|
||||
fieldsize(p));
|
||||
else
|
||||
print(",%d,%d;", 8*p->offset, 8*p->type->size);
|
||||
col += 1+3+1+3+1; /* accounts for ,%d,%d; */
|
||||
if (col >= 80 && p->link) {
|
||||
print("\\\\\",%d,0,0,0\n.stabs \"", N_LSYM);
|
||||
col = 8;
|
||||
}
|
||||
}
|
||||
print(";"), col += 1;
|
||||
break;
|
||||
}
|
||||
case ENUM: {
|
||||
Symbol *p;
|
||||
if (lev > 0 && (*ty->u.sym->name < '0' || *ty->u.sym->name > '9')) {
|
||||
ty->x.printed = 0;
|
||||
break;
|
||||
}
|
||||
print("=e"), col += 2;
|
||||
for (p = ty->u.sym->u.idlist; *p; p++) {
|
||||
print("%s:%d,", (*p)->name, (*p)->u.value), col += strlen((*p)->name)+3;
|
||||
if (col >= 80 && p[1]) {
|
||||
print("\\\\\",%d,0,0,0\n.stabs \"", N_LSYM);
|
||||
col = 8;
|
||||
}
|
||||
}
|
||||
print(";"), col += 1;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
return col;
|
||||
}
|
||||
|
||||
/* stabblock - output a stab entry for '{' or '}' at level lev */
|
||||
void stabblock(int brace, int lev, Symbol *p) {
|
||||
if (brace == '{')
|
||||
while (*p)
|
||||
stabsym(*p++);
|
||||
if (IR == &sparcIR)
|
||||
print(".stabd 0x%x,0,%d\n", brace == '{' ? N_LBRAC : N_RBRAC, lev);
|
||||
else {
|
||||
int lab = genlabel(1);
|
||||
print(".stabn 0x%x,0,%d,%s%d-%s\n", brace == '{' ? N_LBRAC : N_RBRAC, lev,
|
||||
stabprefix, lab, cfunc->x.name);
|
||||
print("%s%d:\n", stabprefix, lab);
|
||||
}
|
||||
}
|
||||
|
||||
/* stabinit - initialize stab output */
|
||||
void stabinit(char *file, int argc, char *argv[]) {
|
||||
typedef void (*Closure)(Symbol, void *);
|
||||
extern char *getcwd(char *, size_t);
|
||||
|
||||
print(".stabs \"lcc4_compiled.\",0x%x,0,0,0\n", N_OPT);
|
||||
if (file && *file) {
|
||||
char buf[1024], *cwd = getcwd(buf, sizeof buf);
|
||||
if (cwd)
|
||||
print(".stabs \"%s/\",0x%x,0,3,%stext0\n", cwd, N_SO, stabprefix);
|
||||
print(".stabs \"%s\",0x%x,0,3,%stext0\n", file, N_SO, stabprefix);
|
||||
(*IR->segment)(CODE);
|
||||
print("%stext0:\n", stabprefix, N_SO);
|
||||
currentfile = file;
|
||||
}
|
||||
dbxtype(inttype);
|
||||
dbxtype(chartype);
|
||||
dbxtype(doubletype);
|
||||
dbxtype(floattype);
|
||||
dbxtype(longdouble);
|
||||
dbxtype(longtype);
|
||||
dbxtype(longlong);
|
||||
dbxtype(shorttype);
|
||||
dbxtype(signedchar);
|
||||
dbxtype(unsignedchar);
|
||||
dbxtype(unsignedlong);
|
||||
dbxtype(unsignedlonglong);
|
||||
dbxtype(unsignedshort);
|
||||
dbxtype(unsignedtype);
|
||||
dbxtype(voidtype);
|
||||
foreach(types, GLOBAL, (Closure)stabtype, NULL);
|
||||
}
|
||||
|
||||
/* stabline - emit stab entry for source coordinate *cp */
|
||||
void stabline(Coordinate *cp) {
|
||||
if (cp->file && cp->file != currentfile) {
|
||||
int lab = genlabel(1);
|
||||
print(".stabs \"%s\",0x%x,0,0,%s%d\n", cp->file, N_SOL, stabprefix, lab);
|
||||
print("%s%d:\n", stabprefix, lab);
|
||||
currentfile = cp->file;
|
||||
}
|
||||
if (IR == &sparcIR)
|
||||
print(".stabd 0x%x,0,%d\n", N_SLINE, cp->y);
|
||||
else {
|
||||
int lab = genlabel(1);
|
||||
print(".stabn 0x%x,0,%d,%s%d-%s\n", N_SLINE, cp->y,
|
||||
stabprefix, lab, cfunc->x.name);
|
||||
print("%s%d:\n", stabprefix, lab);
|
||||
}
|
||||
}
|
||||
|
||||
/* stabsym - output a stab entry for symbol p */
|
||||
void stabsym(Symbol p) {
|
||||
int code, tc, sz = p->type->size;
|
||||
|
||||
if (p->generated || p->computed)
|
||||
return;
|
||||
if (isfunc(p->type)) {
|
||||
print(".stabs \"%s:%c%d\",%d,0,0,%s\n", p->name,
|
||||
p->sclass == STATIC ? 'f' : 'F', dbxtype(freturn(p->type)),
|
||||
N_FUN, p->x.name);
|
||||
return;
|
||||
}
|
||||
if (!IR->wants_argb && p->scope == PARAM && p->structarg) {
|
||||
assert(isptr(p->type) && isstruct(p->type->type));
|
||||
tc = dbxtype(p->type->type);
|
||||
sz = p->type->type->size;
|
||||
} else
|
||||
tc = dbxtype(p->type);
|
||||
if (p->sclass == AUTO && p->scope == GLOBAL || p->sclass == EXTERN) {
|
||||
print(".stabs \"%s:G", p->name);
|
||||
code = N_GSYM;
|
||||
} else if (p->sclass == STATIC) {
|
||||
print(".stabs \"%s:%c%d\",%d,0,0,%s\n", p->name, p->scope == GLOBAL ? 'S' : 'V',
|
||||
tc, p->u.seg == BSS ? N_LCSYM : N_STSYM, p->x.name);
|
||||
return;
|
||||
} else if (p->sclass == REGISTER) {
|
||||
if (p->x.regnode) {
|
||||
int r = p->x.regnode->number;
|
||||
if (p->x.regnode->set == FREG)
|
||||
r += 32; /* floating point */
|
||||
print(".stabs \"%s:%c%d\",%d,0,", p->name,
|
||||
p->scope == PARAM ? 'P' : 'r', tc, N_RSYM);
|
||||
print("%d,%d\n", sz, r);
|
||||
}
|
||||
return;
|
||||
} else if (p->scope == PARAM) {
|
||||
print(".stabs \"%s:p", p->name);
|
||||
code = N_PSYM;
|
||||
} else if (p->scope >= LOCAL) {
|
||||
print(".stabs \"%s:", p->name);
|
||||
code = N_LSYM;
|
||||
} else
|
||||
assert(0);
|
||||
print("%d\",%d,0,0,%s\n", tc, code,
|
||||
p->scope >= PARAM && p->sclass != EXTERN ? p->x.name : "0");
|
||||
}
|
||||
|
||||
/* stabtype - output a stab entry for type *p */
|
||||
void stabtype(Symbol p) {
|
||||
if (p->type) {
|
||||
if (p->sclass == 0)
|
||||
dbxtype(p->type);
|
||||
else if (p->sclass == TYPEDEF)
|
||||
print(".stabs \"%s:t%d\",%d,0,0,0\n", p->name, dbxtype(p->type), N_LSYM);
|
||||
}
|
||||
}
|
||||
|
||||
/* stabend - finalize a function */
|
||||
void stabfend(Symbol p, int lineno) {}
|
||||
|
||||
/* stabend - finalize stab output */
|
||||
void stabend(Coordinate *cp, Symbol p, Coordinate **cpp, Symbol *sp, Symbol *stab) {
|
||||
(*IR->segment)(CODE);
|
||||
print(".stabs \"\", %d, 0, 0,%setext\n", N_SO, stabprefix);
|
||||
print("%setext:\n", stabprefix);
|
||||
}
|
||||
108
src/cmd/lccom-1/stab.h
Normal file
108
src/cmd/lccom-1/stab.h
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (c) 1990 by Sun Microsystems, Inc.
|
||||
*
|
||||
* This file gives definitions supplementing <a.out.h>
|
||||
* for permanent symbol table entries.
|
||||
* These must have one of the N_STAB bits on,
|
||||
* and are subject to relocation according to the masks in <a.out.h>.
|
||||
*/
|
||||
#ifndef _STAB_H
|
||||
#define _STAB_H
|
||||
|
||||
#if !defined(_a_out_h) && !defined(_A_OUT_H)
|
||||
/* this file contains fragments of a.out.h and stab.h relevant to
|
||||
* support of stabX processing within ELF files - see the
|
||||
* Format of a symbol table entry
|
||||
*/
|
||||
struct nlist {
|
||||
union {
|
||||
char *n_name; /* for use when in-core */
|
||||
long n_strx; /* index into file string table */
|
||||
} n_un;
|
||||
unsigned char n_type; /* type flag (N_TEXT,..) */
|
||||
char n_other; /* unused */
|
||||
short n_desc; /* see <stab.h> */
|
||||
unsigned long n_value; /* value of symbol (or sdb offset) */
|
||||
};
|
||||
|
||||
/*
|
||||
* Simple values for n_type.
|
||||
*/
|
||||
#define N_UNDF 0x0 /* undefined */
|
||||
#define N_ABS 0x2 /* absolute */
|
||||
#define N_TEXT 0x4 /* text */
|
||||
#define N_DATA 0x6 /* data */
|
||||
#define N_BSS 0x8 /* bss */
|
||||
#define N_COMM 0x12 /* common (internal to ld) */
|
||||
#define N_FN 0x1f /* file name symbol */
|
||||
|
||||
#define N_EXT 01 /* external bit, or'ed in */
|
||||
#define N_TYPE 0x1e /* mask for all the type bits */
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* for symbolic debugger, sdb(1):
|
||||
*/
|
||||
#define N_GSYM 0x20 /* global symbol: name,,0,type,0 */
|
||||
#define N_FNAME 0x22 /* procedure name (f77 kludge): name,,0 */
|
||||
#define N_FUN 0x24 /* procedure: name,,0,linenumber,address */
|
||||
#define N_STSYM 0x26 /* static symbol: name,,0,type,address */
|
||||
#define N_LCSYM 0x28 /* .lcomm symbol: name,,0,type,address */
|
||||
#define N_MAIN 0x2a /* name of main routine : name,,0,0,0 */
|
||||
#define N_ROSYM 0x2c /* ro_data objects */
|
||||
#define N_OBJ 0x38 /* object file path or name */
|
||||
#define N_OPT 0x3c /* compiler options */
|
||||
#define N_RSYM 0x40 /* register sym: name,,0,type,register */
|
||||
#define N_SLINE 0x44 /* src line: 0,,0,linenumber,address */
|
||||
#define N_FLINE 0x4c /* function start.end */
|
||||
#define N_SSYM 0x60 /* structure elt: name,,0,type,struct_offset */
|
||||
#define N_ENDM 0x62 /* last stab emitted for module */
|
||||
#define N_SO 0x64 /* source file name: name,,0,0,address */
|
||||
#define N_LSYM 0x80 /* local sym: name,,0,type,offset */
|
||||
#define N_BINCL 0x82 /* header file: name,,0,0,0 */
|
||||
#define N_SOL 0x84 /* #included file name: name,,0,0,address */
|
||||
#define N_PSYM 0xa0 /* parameter: name,,0,type,offset */
|
||||
#define N_EINCL 0xa2 /* end of include file */
|
||||
#define N_ENTRY 0xa4 /* alternate entry: name,linenumber,address */
|
||||
#define N_LBRAC 0xc0 /* left bracket: 0,,0,nesting level,address */
|
||||
#define N_EXCL 0xc2 /* excluded include file */
|
||||
#define N_RBRAC 0xe0 /* right bracket: 0,,0,nesting level,address */
|
||||
#define N_BCOMM 0xe2 /* begin common: name,, */
|
||||
#define N_ECOMM 0xe4 /* end common: name,, */
|
||||
#define N_ECOML 0xe8 /* end common (local name): ,,address */
|
||||
#define N_LENG 0xfe /* second stab entry with length information */
|
||||
|
||||
/*
|
||||
* for the berkeley pascal compiler, pc(1):
|
||||
*/
|
||||
#define N_PC 0x30 /* global pascal symbol: name,,0,subtype,line */
|
||||
#define N_WITH 0xea /* pascal with statement: type,,0,0,offset */
|
||||
|
||||
/*
|
||||
* for code browser only
|
||||
*/
|
||||
#define N_BROWS 0x48 /* path to associated .cb file */
|
||||
|
||||
/*
|
||||
* Optional langauge designations for N_SO
|
||||
*/
|
||||
#define N_SO_AS 1 /* Assembler */
|
||||
#define N_SO_C 2 /* C */
|
||||
#define N_SO_ANSI_C 3 /* ANSI C */
|
||||
#define N_SO_CC 4 /* C++ */
|
||||
#define N_SO_FORTRAN 5 /* Fortran 77 */
|
||||
#define N_SO_PASCAL 6 /* Pascal */
|
||||
|
||||
/*
|
||||
* Floating point type values
|
||||
*/
|
||||
#define NF_NONE 0 /* Undefined type */
|
||||
#define NF_SINGLE 1 /* IEEE 32 bit float */
|
||||
#define NF_DOUBLE 2 /* IEEE 64 bit float */
|
||||
#define NF_COMPLEX 3 /* Fortran complex */
|
||||
#define NF_COMPLEX16 4 /* Fortran double complex */
|
||||
#define NF_COMPLEX32 5 /* Fortran complex*16 */
|
||||
#define NF_LDOUBLE 6 /* Long double */
|
||||
|
||||
#endif
|
||||
707
src/cmd/lccom-1/stmt.c
Normal file
707
src/cmd/lccom-1/stmt.c
Normal file
@@ -0,0 +1,707 @@
|
||||
#include "c.h"
|
||||
|
||||
#define SWSIZE 512
|
||||
|
||||
#define den(i,j) ((j-buckets[i]+1.0)/(v[j]-v[buckets[i]]+1))
|
||||
|
||||
struct code codehead = { Start };
|
||||
Code codelist = &codehead;
|
||||
float density = 0.5;
|
||||
Table stmtlabs;
|
||||
|
||||
static int foldcond(Tree e1, Tree e2);
|
||||
static void caselabel(Swtch, long, int);
|
||||
static void cmp(int, Symbol, long, int);
|
||||
static Tree conditional(int);
|
||||
static void dostmt(int, Swtch, int);
|
||||
static int equal(Symbol, Symbol);
|
||||
static void forstmt(int, Swtch, int);
|
||||
static void ifstmt(int, int, Swtch, int);
|
||||
static Symbol localaddr(Tree);
|
||||
static void stmtlabel(void);
|
||||
static void swstmt(int, int, int);
|
||||
static void whilestmt(int, Swtch, int);
|
||||
Code code(int kind) {
|
||||
Code cp;
|
||||
|
||||
if (!reachable(kind))
|
||||
warning("unreachable code\n");
|
||||
|
||||
NEW(cp, FUNC);
|
||||
cp->kind = kind;
|
||||
cp->prev = codelist;
|
||||
cp->next = NULL;
|
||||
codelist->next = cp;
|
||||
codelist = cp;
|
||||
return cp;
|
||||
}
|
||||
|
||||
int reachable(int kind) {
|
||||
if (kind > Start) {
|
||||
Code cp;
|
||||
for (cp = codelist; cp->kind < Label; )
|
||||
cp = cp->prev;
|
||||
if (cp->kind == Jump || cp->kind == Switch)
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void addlocal(Symbol p) {
|
||||
if (!p->defined) {
|
||||
code(Local)->u.var = p;
|
||||
p->defined = 1;
|
||||
p->scope = level;
|
||||
}
|
||||
}
|
||||
void definept(Coordinate *p) {
|
||||
Code cp = code(Defpoint);
|
||||
|
||||
cp->u.point.src = p ? *p : src;
|
||||
cp->u.point.point = npoints;
|
||||
if (ncalled > 0) {
|
||||
int n = findcount(cp->u.point.src.file,
|
||||
cp->u.point.src.x, cp->u.point.src.y);
|
||||
if (n > 0)
|
||||
refinc = (float)n/ncalled;
|
||||
}
|
||||
if (glevel > 2) locus(identifiers, &cp->u.point.src);
|
||||
if (events.points && reachable(Gen))
|
||||
{
|
||||
Tree e = NULL;
|
||||
apply(events.points, &cp->u.point.src, &e);
|
||||
if (e)
|
||||
listnodes(e, 0, 0);
|
||||
}
|
||||
}
|
||||
void statement(int loop, Swtch swp, int lev) {
|
||||
float ref = refinc;
|
||||
|
||||
if (Aflag >= 2 && lev == 15)
|
||||
warning("more than 15 levels of nested statements\n");
|
||||
switch (t) {
|
||||
case IF: ifstmt(genlabel(2), loop, swp, lev + 1);
|
||||
break;
|
||||
case WHILE: whilestmt(genlabel(3), swp, lev + 1); break;
|
||||
case DO: dostmt(genlabel(3), swp, lev + 1); expect(';');
|
||||
break;
|
||||
|
||||
case FOR: forstmt(genlabel(4), swp, lev + 1);
|
||||
break;
|
||||
case BREAK: walk(NULL, 0, 0);
|
||||
definept(NULL);
|
||||
if (swp && swp->lab > loop)
|
||||
branch(swp->lab + 1);
|
||||
else if (loop)
|
||||
branch(loop + 2);
|
||||
else
|
||||
error("illegal break statement\n");
|
||||
t = gettok(); expect(';');
|
||||
break;
|
||||
|
||||
case CONTINUE: walk(NULL, 0, 0);
|
||||
definept(NULL);
|
||||
if (loop)
|
||||
branch(loop + 1);
|
||||
else
|
||||
error("illegal continue statement\n");
|
||||
t = gettok(); expect(';');
|
||||
break;
|
||||
|
||||
case SWITCH: swstmt(loop, genlabel(2), lev + 1);
|
||||
break;
|
||||
case CASE: {
|
||||
int lab = genlabel(1);
|
||||
if (swp == NULL)
|
||||
error("illegal case label\n");
|
||||
definelab(lab);
|
||||
while (t == CASE) {
|
||||
static char stop[] = { IF, ID, 0 };
|
||||
Tree p;
|
||||
t = gettok();
|
||||
p = constexpr(0);
|
||||
if (generic(p->op) == CNST && isint(p->type)) {
|
||||
if (swp) {
|
||||
needconst++;
|
||||
p = cast(p, swp->sym->type);
|
||||
if (p->type->op == UNSIGNED)
|
||||
p->u.v.i = extend(p->u.v.u, p->type);
|
||||
needconst--;
|
||||
caselabel(swp, p->u.v.i, lab);
|
||||
}
|
||||
} else
|
||||
error("case label must be a constant integer expression\n");
|
||||
|
||||
test(':', stop);
|
||||
}
|
||||
statement(loop, swp, lev);
|
||||
} break;
|
||||
case DEFAULT: if (swp == NULL)
|
||||
error("illegal default label\n");
|
||||
else if (swp->deflab)
|
||||
error("extra default label\n");
|
||||
else {
|
||||
swp->deflab = findlabel(swp->lab);
|
||||
definelab(swp->deflab->u.l.label);
|
||||
}
|
||||
t = gettok();
|
||||
expect(':');
|
||||
statement(loop, swp, lev); break;
|
||||
case RETURN: {
|
||||
Type rty = freturn(cfunc->type);
|
||||
t = gettok();
|
||||
definept(NULL);
|
||||
if (t != ';')
|
||||
if (rty == voidtype) {
|
||||
error("extraneous return value\n");
|
||||
expr(0);
|
||||
retcode(NULL);
|
||||
} else
|
||||
retcode(expr(0));
|
||||
else {
|
||||
if (rty != voidtype) {
|
||||
warning("missing return value\n");
|
||||
retcode(cnsttree(inttype, 0L));
|
||||
} else
|
||||
retcode(NULL);
|
||||
}
|
||||
branch(cfunc->u.f.label);
|
||||
} expect(';');
|
||||
break;
|
||||
|
||||
case '{': compound(loop, swp, lev + 1); break;
|
||||
case ';': definept(NULL); t = gettok(); break;
|
||||
case GOTO: walk(NULL, 0, 0);
|
||||
definept(NULL);
|
||||
t = gettok();
|
||||
if (t == ID) {
|
||||
Symbol p = lookup(token, stmtlabs);
|
||||
if (p == NULL) {
|
||||
p = install(token, &stmtlabs, 0, FUNC);
|
||||
p->scope = LABELS;
|
||||
p->u.l.label = genlabel(1);
|
||||
p->src = src;
|
||||
}
|
||||
use(p, src);
|
||||
branch(p->u.l.label);
|
||||
t = gettok();
|
||||
} else
|
||||
error("missing label in goto\n"); expect(';');
|
||||
break;
|
||||
|
||||
case ID: if (getchr() == ':') {
|
||||
stmtlabel();
|
||||
statement(loop, swp, lev);
|
||||
break;
|
||||
}
|
||||
default: definept(NULL);
|
||||
if (kind[t] != ID) {
|
||||
error("unrecognized statement\n");
|
||||
t = gettok();
|
||||
} else {
|
||||
Tree e = expr0(0);
|
||||
listnodes(e, 0, 0);
|
||||
if (nodecount == 0 || nodecount > 200)
|
||||
walk(NULL, 0, 0);
|
||||
else if (glevel) walk(NULL, 0, 0);
|
||||
deallocate(STMT);
|
||||
} expect(';');
|
||||
break;
|
||||
|
||||
}
|
||||
if (kind[t] != IF && kind[t] != ID
|
||||
&& t != '}' && t != EOI) {
|
||||
static char stop[] = { IF, ID, '}', 0 };
|
||||
error("illegal statement termination\n");
|
||||
skipto(0, stop);
|
||||
}
|
||||
refinc = ref;
|
||||
}
|
||||
|
||||
static void ifstmt(int lab, int loop, Swtch swp, int lev) {
|
||||
t = gettok();
|
||||
expect('(');
|
||||
definept(NULL);
|
||||
walk(conditional(')'), 0, lab);
|
||||
refinc /= 2.0;
|
||||
statement(loop, swp, lev);
|
||||
if (t == ELSE) {
|
||||
branch(lab + 1);
|
||||
t = gettok();
|
||||
definelab(lab);
|
||||
statement(loop, swp, lev);
|
||||
if (findlabel(lab + 1)->ref)
|
||||
definelab(lab + 1);
|
||||
} else
|
||||
definelab(lab);
|
||||
}
|
||||
static Tree conditional(int tok) {
|
||||
Tree p = expr(tok);
|
||||
|
||||
if (Aflag > 1 && isfunc(p->type))
|
||||
warning("%s used in a conditional expression\n",
|
||||
funcname(p));
|
||||
return cond(p);
|
||||
}
|
||||
static void stmtlabel(void) {
|
||||
Symbol p = lookup(token, stmtlabs);
|
||||
|
||||
if (p == NULL) {
|
||||
p = install(token, &stmtlabs, 0, FUNC);
|
||||
p->scope = LABELS;
|
||||
p->u.l.label = genlabel(1);
|
||||
p->src = src;
|
||||
}
|
||||
if (p->defined)
|
||||
error("redefinition of label `%s' previously defined at %w\n", p->name, &p->src);
|
||||
|
||||
p->defined = 1;
|
||||
definelab(p->u.l.label);
|
||||
t = gettok();
|
||||
expect(':');
|
||||
}
|
||||
static void forstmt(int lab, Swtch swp, int lev) {
|
||||
int once = 0;
|
||||
Tree e1 = NULL, e2 = NULL, e3 = NULL;
|
||||
Coordinate pt2, pt3;
|
||||
|
||||
t = gettok();
|
||||
expect('(');
|
||||
definept(NULL);
|
||||
if (kind[t] == ID)
|
||||
e1 = texpr(expr0, ';', FUNC);
|
||||
else
|
||||
expect(';');
|
||||
walk(e1, 0, 0);
|
||||
pt2 = src;
|
||||
refinc *= 10.0;
|
||||
if (kind[t] == ID)
|
||||
e2 = texpr(conditional, ';', FUNC);
|
||||
else
|
||||
expect(';');
|
||||
pt3 = src;
|
||||
if (kind[t] == ID)
|
||||
e3 = texpr(expr0, ')', FUNC);
|
||||
else {
|
||||
static char stop[] = { IF, ID, '}', 0 };
|
||||
test(')', stop);
|
||||
}
|
||||
if (e2) {
|
||||
once = foldcond(e1, e2);
|
||||
if (!once)
|
||||
branch(lab + 3);
|
||||
}
|
||||
definelab(lab);
|
||||
statement(lab, swp, lev);
|
||||
definelab(lab + 1);
|
||||
definept(&pt3);
|
||||
if (e3)
|
||||
walk(e3, 0, 0);
|
||||
if (e2) {
|
||||
if (!once)
|
||||
definelab(lab + 3);
|
||||
definept(&pt2);
|
||||
walk(e2, lab, 0);
|
||||
} else {
|
||||
definept(&pt2);
|
||||
branch(lab);
|
||||
}
|
||||
if (findlabel(lab + 2)->ref)
|
||||
definelab(lab + 2);
|
||||
}
|
||||
static void swstmt(int loop, int lab, int lev) {
|
||||
Tree e;
|
||||
struct swtch sw;
|
||||
Code head, tail;
|
||||
|
||||
t = gettok();
|
||||
expect('(');
|
||||
definept(NULL);
|
||||
e = expr(')');
|
||||
if (!isint(e->type)) {
|
||||
error("illegal type `%t' in switch expression\n",
|
||||
e->type);
|
||||
e = retype(e, inttype);
|
||||
}
|
||||
e = cast(e, promote(e->type));
|
||||
if (generic(e->op) == INDIR && isaddrop(e->kids[0]->op)
|
||||
&& e->kids[0]->u.sym->type == e->type
|
||||
&& !isvolatile(e->kids[0]->u.sym->type)) {
|
||||
sw.sym = e->kids[0]->u.sym;
|
||||
walk(NULL, 0, 0);
|
||||
} else {
|
||||
sw.sym = genident(REGISTER, e->type, level);
|
||||
addlocal(sw.sym);
|
||||
walk(asgn(sw.sym, e), 0, 0);
|
||||
}
|
||||
head = code(Switch);
|
||||
sw.lab = lab;
|
||||
sw.deflab = NULL;
|
||||
sw.ncases = 0;
|
||||
sw.size = SWSIZE;
|
||||
sw.values = newarray(SWSIZE, sizeof *sw.values, FUNC);
|
||||
sw.labels = newarray(SWSIZE, sizeof *sw.labels, FUNC);
|
||||
refinc /= 10.0;
|
||||
statement(loop, &sw, lev);
|
||||
if (sw.deflab == NULL) {
|
||||
sw.deflab = findlabel(lab);
|
||||
definelab(lab);
|
||||
if (sw.ncases == 0)
|
||||
warning("switch statement with no cases\n");
|
||||
}
|
||||
if (findlabel(lab + 1)->ref)
|
||||
definelab(lab + 1);
|
||||
tail = codelist;
|
||||
codelist = head->prev;
|
||||
codelist->next = head->prev = NULL;
|
||||
if (sw.ncases > 0)
|
||||
swgen(&sw);
|
||||
branch(lab);
|
||||
head->next->prev = codelist;
|
||||
codelist->next = head->next;
|
||||
codelist = tail;
|
||||
}
|
||||
static void caselabel(Swtch swp, long val, int lab) {
|
||||
int k;
|
||||
|
||||
if (swp->ncases >= swp->size)
|
||||
{
|
||||
long *vals = swp->values;
|
||||
Symbol *labs = swp->labels;
|
||||
swp->size *= 2;
|
||||
swp->values = newarray(swp->size, sizeof *swp->values, FUNC);
|
||||
swp->labels = newarray(swp->size, sizeof *swp->labels, FUNC);
|
||||
for (k = 0; k < swp->ncases; k++) {
|
||||
swp->values[k] = vals[k];
|
||||
swp->labels[k] = labs[k];
|
||||
}
|
||||
}
|
||||
k = swp->ncases;
|
||||
for ( ; k > 0 && swp->values[k-1] >= val; k--) {
|
||||
swp->values[k] = swp->values[k-1];
|
||||
swp->labels[k] = swp->labels[k-1];
|
||||
}
|
||||
if (k < swp->ncases && swp->values[k] == val)
|
||||
error("duplicate case label `%d'\n", val);
|
||||
swp->values[k] = val;
|
||||
swp->labels[k] = findlabel(lab);
|
||||
++swp->ncases;
|
||||
if (Aflag >= 2 && swp->ncases == 258)
|
||||
warning("more than 257 cases in a switch\n");
|
||||
}
|
||||
void swgen(Swtch swp) {
|
||||
int *buckets, k, n;
|
||||
long *v = swp->values;
|
||||
|
||||
buckets = newarray(swp->ncases + 1,
|
||||
sizeof *buckets, FUNC);
|
||||
for (n = k = 0; k < swp->ncases; k++, n++) {
|
||||
buckets[n] = k;
|
||||
while (n > 0 && den(n-1, k) >= density)
|
||||
n--;
|
||||
}
|
||||
buckets[n] = swp->ncases;
|
||||
swcode(swp, buckets, 0, n - 1);
|
||||
}
|
||||
void swcode(Swtch swp, int b[], int lb, int ub) {
|
||||
int hilab, lolab, l, u, k = (lb + ub)/2;
|
||||
long *v = swp->values;
|
||||
|
||||
if (k > lb && k < ub) {
|
||||
lolab = genlabel(1);
|
||||
hilab = genlabel(1);
|
||||
} else if (k > lb) {
|
||||
lolab = genlabel(1);
|
||||
hilab = swp->deflab->u.l.label;
|
||||
} else if (k < ub) {
|
||||
lolab = swp->deflab->u.l.label;
|
||||
hilab = genlabel(1);
|
||||
} else
|
||||
lolab = hilab = swp->deflab->u.l.label;
|
||||
l = b[k];
|
||||
u = b[k+1] - 1;
|
||||
if (u - l + 1 <= 3)
|
||||
{
|
||||
int i;
|
||||
for (i = l; i <= u; i++)
|
||||
cmp(EQ, swp->sym, v[i], swp->labels[i]->u.l.label);
|
||||
if (k > lb && k < ub)
|
||||
cmp(GT, swp->sym, v[u], hilab);
|
||||
else if (k > lb)
|
||||
cmp(GT, swp->sym, v[u], hilab);
|
||||
else if (k < ub)
|
||||
cmp(LT, swp->sym, v[l], lolab);
|
||||
else {
|
||||
assert(lolab == hilab);
|
||||
branch(lolab);
|
||||
}
|
||||
walk(NULL, 0, 0);
|
||||
}
|
||||
else {
|
||||
Tree e;
|
||||
Type ty = signedint(swp->sym->type);
|
||||
Symbol table = genident(STATIC,
|
||||
array(voidptype, u - l + 1, 0), GLOBAL);
|
||||
(*IR->defsymbol)(table);
|
||||
if (!isunsigned(swp->sym->type) || v[l] != 0)
|
||||
cmp(LT, swp->sym, v[l], lolab);
|
||||
cmp(GT, swp->sym, v[u], hilab);
|
||||
e = (*optree['-'])(SUB, cast(idtree(swp->sym), ty), cnsttree(ty, v[l]));
|
||||
if (e->type->size < unsignedptr->size)
|
||||
e = cast(e, unsignedlong);
|
||||
walk(tree(JUMP, voidtype,
|
||||
rvalue((*optree['+'])(ADD, pointer(idtree(table)), e)), NULL),
|
||||
0, 0);
|
||||
code(Switch);
|
||||
codelist->u.swtch.table = table;
|
||||
codelist->u.swtch.sym = swp->sym;
|
||||
codelist->u.swtch.deflab = swp->deflab;
|
||||
codelist->u.swtch.size = u - l + 1;
|
||||
codelist->u.swtch.values = &v[l];
|
||||
codelist->u.swtch.labels = &swp->labels[l];
|
||||
if (v[u] - v[l] + 1 >= 10000)
|
||||
warning("switch generates a huge table\n");
|
||||
}
|
||||
if (k > lb) {
|
||||
assert(lolab != swp->deflab->u.l.label);
|
||||
definelab(lolab);
|
||||
swcode(swp, b, lb, k - 1);
|
||||
}
|
||||
if (k < ub) {
|
||||
assert(hilab != swp->deflab->u.l.label);
|
||||
definelab(hilab);
|
||||
swcode(swp, b, k + 1, ub);
|
||||
}
|
||||
}
|
||||
static void cmp(int op, Symbol p, long n, int lab) {
|
||||
Type ty = signedint(p->type);
|
||||
|
||||
listnodes(eqtree(op,
|
||||
cast(idtree(p), ty),
|
||||
cnsttree(ty, n)),
|
||||
lab, 0);
|
||||
}
|
||||
void retcode(Tree p) {
|
||||
Type ty;
|
||||
|
||||
if (p == NULL) {
|
||||
if (events.returns)
|
||||
apply(events.returns, cfunc, NULL);
|
||||
return;
|
||||
}
|
||||
p = pointer(p);
|
||||
ty = assign(freturn(cfunc->type), p);
|
||||
if (ty == NULL) {
|
||||
error("illegal return type; found `%t' expected `%t'\n",
|
||||
p->type, freturn(cfunc->type));
|
||||
return;
|
||||
}
|
||||
p = cast(p, ty);
|
||||
if (retv)
|
||||
{
|
||||
if (iscallb(p))
|
||||
p = tree(RIGHT, p->type,
|
||||
tree(CALL+B, p->type,
|
||||
p->kids[0]->kids[0], idtree(retv)),
|
||||
rvalue(idtree(retv)));
|
||||
else {
|
||||
Type ty = retv->type->type;
|
||||
assert(isstruct(ty));
|
||||
if (ty->u.sym->u.s.cfields) {
|
||||
ty->u.sym->u.s.cfields = 0;
|
||||
p = asgntree(ASGN, rvalue(idtree(retv)), p);
|
||||
ty->u.sym->u.s.cfields = 1;
|
||||
} else
|
||||
p = asgntree(ASGN, rvalue(idtree(retv)), p);
|
||||
}
|
||||
walk(p, 0, 0);
|
||||
if (events.returns)
|
||||
apply(events.returns, cfunc, rvalue(idtree(retv)));
|
||||
return;
|
||||
}
|
||||
if (events.returns)
|
||||
{
|
||||
Symbol t1 = genident(AUTO, p->type, level);
|
||||
addlocal(t1);
|
||||
walk(asgn(t1, p), 0, 0);
|
||||
apply(events.returns, cfunc, idtree(t1));
|
||||
p = idtree(t1);
|
||||
}
|
||||
if (!isfloat(p->type))
|
||||
p = cast(p, promote(p->type));
|
||||
if (isptr(p->type))
|
||||
{
|
||||
Symbol q = localaddr(p);
|
||||
if (q && (q->computed || q->generated))
|
||||
warning("pointer to a %s is an illegal return value\n",
|
||||
q->scope == PARAM ? "parameter" : "local");
|
||||
else if (q)
|
||||
warning("pointer to %s `%s' is an illegal return value\n",
|
||||
q->scope == PARAM ? "parameter" : "local", q->name);
|
||||
}
|
||||
walk(tree(mkop(RET,p->type), p->type, p, NULL), 0, 0);
|
||||
}
|
||||
void definelab(int lab) {
|
||||
Code cp;
|
||||
Symbol p = findlabel(lab);
|
||||
|
||||
assert(lab);
|
||||
walk(NULL, 0, 0);
|
||||
code(Label)->u.forest = newnode(LABEL+V, NULL, NULL, p);
|
||||
for (cp = codelist->prev; cp->kind <= Label; )
|
||||
cp = cp->prev;
|
||||
while ( cp->kind == Jump
|
||||
&& cp->u.forest->kids[0]
|
||||
&& specific(cp->u.forest->kids[0]->op) == ADDRG+P
|
||||
&& cp->u.forest->kids[0]->syms[0] == p) {
|
||||
assert(cp->u.forest->kids[0]->syms[0]->u.l.label == lab);
|
||||
p->ref--;
|
||||
assert(cp->next);
|
||||
assert(cp->prev);
|
||||
cp->prev->next = cp->next;
|
||||
cp->next->prev = cp->prev;
|
||||
cp = cp->prev;
|
||||
while (cp->kind <= Label)
|
||||
cp = cp->prev;
|
||||
}
|
||||
}
|
||||
Node jump(int lab) {
|
||||
Symbol p = findlabel(lab);
|
||||
|
||||
p->ref++;
|
||||
return newnode(JUMP+V, newnode(ADDRG+ttob(voidptype), NULL, NULL, p),
|
||||
NULL, NULL);
|
||||
}
|
||||
void branch(int lab) {
|
||||
Code cp;
|
||||
Symbol p = findlabel(lab);
|
||||
|
||||
assert(lab);
|
||||
walk(NULL, 0, 0);
|
||||
code(Label)->u.forest = jump(lab);
|
||||
for (cp = codelist->prev; cp->kind < Label; )
|
||||
cp = cp->prev;
|
||||
while ( cp->kind == Label
|
||||
&& cp->u.forest->op == LABEL+V
|
||||
&& !equal(cp->u.forest->syms[0], p)) {
|
||||
equatelab(cp->u.forest->syms[0], p);
|
||||
assert(cp->next);
|
||||
assert(cp->prev);
|
||||
cp->prev->next = cp->next;
|
||||
cp->next->prev = cp->prev;
|
||||
cp = cp->prev;
|
||||
while (cp->kind < Label)
|
||||
cp = cp->prev;
|
||||
}
|
||||
if (cp->kind == Jump || cp->kind == Switch) {
|
||||
p->ref--;
|
||||
codelist->prev->next = NULL;
|
||||
codelist = codelist->prev;
|
||||
} else {
|
||||
codelist->kind = Jump;
|
||||
if (cp->kind == Label
|
||||
&& cp->u.forest->op == LABEL+V
|
||||
&& equal(cp->u.forest->syms[0], p))
|
||||
warning("source code specifies an infinite loop\n");
|
||||
}
|
||||
}
|
||||
void equatelab(Symbol old, Symbol new) {
|
||||
assert(old->u.l.equatedto == NULL);
|
||||
old->u.l.equatedto = new;
|
||||
new->ref++;
|
||||
}
|
||||
static int equal(Symbol lprime, Symbol dst) {
|
||||
assert(dst && lprime);
|
||||
for ( ; dst; dst = dst->u.l.equatedto)
|
||||
if (lprime == dst)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
/* dostmt - do statement while ( expression ) */
|
||||
static void dostmt(int lab, Swtch swp, int lev) {
|
||||
refinc *= 10.0;
|
||||
t = gettok();
|
||||
definelab(lab);
|
||||
statement(lab, swp, lev);
|
||||
definelab(lab + 1);
|
||||
expect(WHILE);
|
||||
expect('(');
|
||||
definept(NULL);
|
||||
walk(conditional(')'), lab, 0);
|
||||
if (findlabel(lab + 2)->ref)
|
||||
definelab(lab + 2);
|
||||
}
|
||||
|
||||
/* foldcond - check if initial test in for(e1;e2;e3) S is necessary */
|
||||
static int foldcond(Tree e1, Tree e2) {
|
||||
int op = generic(e2->op);
|
||||
Symbol v;
|
||||
|
||||
if (e1 == 0 || e2 == 0)
|
||||
return 0;
|
||||
if (generic(e1->op) == ASGN && isaddrop(e1->kids[0]->op)
|
||||
&& generic(e1->kids[1]->op) == CNST) {
|
||||
v = e1->kids[0]->u.sym;
|
||||
e1 = e1->kids[1];
|
||||
} else
|
||||
return 0;
|
||||
if ((op==LE || op==LT || op==EQ || op==NE || op==GT || op==GE)
|
||||
&& generic(e2->kids[0]->op) == INDIR
|
||||
&& e2->kids[0]->kids[0]->u.sym == v
|
||||
&& e2->kids[1]->op == e1->op) {
|
||||
e1 = simplify(op, e2->type, e1, e2->kids[1]);
|
||||
if (e1->op == CNST+I)
|
||||
return e1->u.v.i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* localaddr - returns q if p yields the address of local/parameter q; otherwise returns 0 */
|
||||
static Symbol localaddr(Tree p) {
|
||||
if (p == NULL)
|
||||
return NULL;
|
||||
switch (generic(p->op)) {
|
||||
case INDIR: case CALL: case ARG:
|
||||
return NULL;
|
||||
case ADDRL: case ADDRF:
|
||||
return p->u.sym;
|
||||
case RIGHT: case ASGN:
|
||||
if (p->kids[1])
|
||||
return localaddr(p->kids[1]);
|
||||
return localaddr(p->kids[0]);
|
||||
case COND: {
|
||||
Symbol q;
|
||||
assert(p->kids[1] && p->kids[1]->op == RIGHT);
|
||||
if ((q = localaddr(p->kids[1]->kids[0])) != NULL)
|
||||
return q;
|
||||
return localaddr(p->kids[1]->kids[1]);
|
||||
}
|
||||
default: {
|
||||
Symbol q;
|
||||
if (p->kids[0] && (q = localaddr(p->kids[0])) != NULL)
|
||||
return q;
|
||||
return localaddr(p->kids[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* whilestmt - while ( expression ) statement */
|
||||
static void whilestmt(int lab, Swtch swp, int lev) {
|
||||
Coordinate pt;
|
||||
Tree e;
|
||||
|
||||
refinc *= 10.0;
|
||||
t = gettok();
|
||||
expect('(');
|
||||
walk(NULL, 0, 0);
|
||||
pt = src;
|
||||
e = texpr(conditional, ')', FUNC);
|
||||
branch(lab + 1);
|
||||
definelab(lab);
|
||||
statement(lab, swp, lev);
|
||||
definelab(lab + 1);
|
||||
definept(&pt);
|
||||
walk(e, lab, 0);
|
||||
if (findlabel(lab + 2)->ref)
|
||||
definelab(lab + 2);
|
||||
}
|
||||
125
src/cmd/lccom-1/string.c
Normal file
125
src/cmd/lccom-1/string.c
Normal file
@@ -0,0 +1,125 @@
|
||||
#include "c.h"
|
||||
|
||||
static struct string {
|
||||
char *str;
|
||||
int len;
|
||||
struct string *link;
|
||||
} *buckets[1024];
|
||||
|
||||
static int scatter[] = { /* map characters to random values */
|
||||
2078917053, 143302914, 1027100827, 1953210302, 755253631,
|
||||
2002600785, 1405390230, 45248011, 1099951567, 433832350,
|
||||
2018585307, 438263339, 813528929, 1703199216, 618906479,
|
||||
573714703, 766270699, 275680090, 1510320440, 1583583926,
|
||||
1723401032, 1965443329, 1098183682, 1636505764, 980071615,
|
||||
1011597961, 643279273, 1315461275, 157584038, 1069844923,
|
||||
471560540, 89017443, 1213147837, 1498661368, 2042227746,
|
||||
1968401469, 1353778505, 1300134328, 2013649480, 306246424,
|
||||
1733966678, 1884751139, 744509763, 400011959, 1440466707,
|
||||
1363416242, 973726663, 59253759, 1639096332, 336563455,
|
||||
1642837685, 1215013716, 154523136, 593537720, 704035832,
|
||||
1134594751, 1605135681, 1347315106, 302572379, 1762719719,
|
||||
269676381, 774132919, 1851737163, 1482824219, 125310639,
|
||||
1746481261, 1303742040, 1479089144, 899131941, 1169907872,
|
||||
1785335569, 485614972, 907175364, 382361684, 885626931,
|
||||
200158423, 1745777927, 1859353594, 259412182, 1237390611,
|
||||
48433401, 1902249868, 304920680, 202956538, 348303940,
|
||||
1008956512, 1337551289, 1953439621, 208787970, 1640123668,
|
||||
1568675693, 478464352, 266772940, 1272929208, 1961288571,
|
||||
392083579, 871926821, 1117546963, 1871172724, 1771058762,
|
||||
139971187, 1509024645, 109190086, 1047146551, 1891386329,
|
||||
994817018, 1247304975, 1489680608, 706686964, 1506717157,
|
||||
579587572, 755120366, 1261483377, 884508252, 958076904,
|
||||
1609787317, 1893464764, 148144545, 1415743291, 2102252735,
|
||||
1788268214, 836935336, 433233439, 2055041154, 2109864544,
|
||||
247038362, 299641085, 834307717, 1364585325, 23330161,
|
||||
457882831, 1504556512, 1532354806, 567072918, 404219416,
|
||||
1276257488, 1561889936, 1651524391, 618454448, 121093252,
|
||||
1010757900, 1198042020, 876213618, 124757630, 2082550272,
|
||||
1834290522, 1734544947, 1828531389, 1982435068, 1002804590,
|
||||
1783300476, 1623219634, 1839739926, 69050267, 1530777140,
|
||||
1802120822, 316088629, 1830418225, 488944891, 1680673954,
|
||||
1853748387, 946827723, 1037746818, 1238619545, 1513900641,
|
||||
1441966234, 367393385, 928306929, 946006977, 985847834,
|
||||
1049400181, 1956764878, 36406206, 1925613800, 2081522508,
|
||||
2118956479, 1612420674, 1668583807, 1800004220, 1447372094,
|
||||
523904750, 1435821048, 923108080, 216161028, 1504871315,
|
||||
306401572, 2018281851, 1820959944, 2136819798, 359743094,
|
||||
1354150250, 1843084537, 1306570817, 244413420, 934220434,
|
||||
672987810, 1686379655, 1301613820, 1601294739, 484902984,
|
||||
139978006, 503211273, 294184214, 176384212, 281341425,
|
||||
228223074, 147857043, 1893762099, 1896806882, 1947861263,
|
||||
1193650546, 273227984, 1236198663, 2116758626, 489389012,
|
||||
593586330, 275676551, 360187215, 267062626, 265012701,
|
||||
719930310, 1621212876, 2108097238, 2026501127, 1865626297,
|
||||
894834024, 552005290, 1404522304, 48964196, 5816381,
|
||||
1889425288, 188942202, 509027654, 36125855, 365326415,
|
||||
790369079, 264348929, 513183458, 536647531, 13672163,
|
||||
313561074, 1730298077, 286900147, 1549759737, 1699573055,
|
||||
776289160, 2143346068, 1975249606, 1136476375, 262925046,
|
||||
92778659, 1856406685, 1884137923, 53392249, 1735424165,
|
||||
1602280572
|
||||
};
|
||||
|
||||
char *string(const char *str) {
|
||||
const char *s;
|
||||
|
||||
for (s = str; *s; s++)
|
||||
;
|
||||
return stringn(str, s - str);
|
||||
}
|
||||
|
||||
char *stringd(long n) {
|
||||
char str[25], *s = str + sizeof (str);
|
||||
unsigned long m;
|
||||
|
||||
if (n == LONG_MIN)
|
||||
m = (unsigned long)LONG_MAX + 1;
|
||||
else if (n < 0)
|
||||
m = -n;
|
||||
else
|
||||
m = n;
|
||||
do
|
||||
*--s = m%10 + '0';
|
||||
while ((m /= 10) != 0);
|
||||
if (n < 0)
|
||||
*--s = '-';
|
||||
return stringn(s, str + sizeof (str) - s);
|
||||
}
|
||||
|
||||
char *stringn(const char *str, int len) {
|
||||
int i;
|
||||
unsigned int h;
|
||||
const char *end;
|
||||
struct string *p;
|
||||
|
||||
assert(str);
|
||||
for (h = 0, i = len, end = str; i > 0; i--)
|
||||
h = (h<<1) + scatter[*(unsigned char *)end++];
|
||||
h &= NELEMS(buckets)-1;
|
||||
for (p = buckets[h]; p; p = p->link)
|
||||
if (len == p->len) {
|
||||
const char *s1 = str;
|
||||
char *s2 = p->str;
|
||||
do {
|
||||
if (s1 == end)
|
||||
return p->str;
|
||||
} while (*s1++ == *s2++);
|
||||
}
|
||||
{
|
||||
static char *next, *strlimit;
|
||||
if (len + 1 >= strlimit - next) {
|
||||
int n = len + 4*1024;
|
||||
next = allocate(n, PERM);
|
||||
strlimit = next + n;
|
||||
}
|
||||
NEW(p, PERM);
|
||||
p->len = len;
|
||||
for (p->str = next; str < end; )
|
||||
*next++ = *str++;
|
||||
*next++ = 0;
|
||||
p->link = buckets[h];
|
||||
buckets[h] = p;
|
||||
return p->str;
|
||||
}
|
||||
}
|
||||
334
src/cmd/lccom-1/sym.c
Normal file
334
src/cmd/lccom-1/sym.c
Normal file
@@ -0,0 +1,334 @@
|
||||
#include "c.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#define equalp(x) v.x == p->sym.u.c.v.x
|
||||
|
||||
struct table {
|
||||
int level;
|
||||
Table previous;
|
||||
struct entry {
|
||||
struct symbol sym;
|
||||
struct entry *link;
|
||||
} *buckets[256];
|
||||
Symbol all;
|
||||
};
|
||||
|
||||
#define HASHSIZE NELEMS(((Table)0)->buckets)
|
||||
|
||||
static struct table
|
||||
cns = { CONSTANTS },
|
||||
ext = { GLOBAL },
|
||||
ids = { GLOBAL },
|
||||
tys = { GLOBAL };
|
||||
Table constants = &cns;
|
||||
Table externals = &ext;
|
||||
Table identifiers = &ids;
|
||||
Table globals = &ids;
|
||||
Table types = &tys;
|
||||
Table labels;
|
||||
int level = GLOBAL;
|
||||
static int tempid;
|
||||
List loci, symbols;
|
||||
|
||||
Table newtable(int arena) {
|
||||
Table new;
|
||||
|
||||
NEW0(new, arena);
|
||||
return new;
|
||||
}
|
||||
|
||||
Table table(Table tp, int level) {
|
||||
Table new = newtable(FUNC);
|
||||
new->previous = tp;
|
||||
new->level = level;
|
||||
if (tp)
|
||||
new->all = tp->all;
|
||||
return new;
|
||||
}
|
||||
void foreach(Table tp, int lev, void (*apply)(Symbol, void *), void *cl) {
|
||||
assert(tp);
|
||||
while (tp && tp->level > lev)
|
||||
tp = tp->previous;
|
||||
if (tp && tp->level == lev) {
|
||||
Symbol p;
|
||||
Coordinate sav;
|
||||
sav = src;
|
||||
for (p = tp->all; p && p->scope == lev; p = p->up) {
|
||||
src = p->src;
|
||||
(*apply)(p, cl);
|
||||
}
|
||||
src = sav;
|
||||
}
|
||||
}
|
||||
void enterscope(void) {
|
||||
if (++level == LOCAL)
|
||||
tempid = 0;
|
||||
}
|
||||
void exitscope(void) {
|
||||
rmtypes(level);
|
||||
if (types->level == level)
|
||||
types = types->previous;
|
||||
if (identifiers->level == level) {
|
||||
if (Aflag >= 2) {
|
||||
int n = 0;
|
||||
Symbol p;
|
||||
for (p = identifiers->all; p && p->scope == level; p = p->up)
|
||||
if (++n > 127) {
|
||||
warning("more than 127 identifiers declared in a block\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
identifiers = identifiers->previous;
|
||||
}
|
||||
assert(level >= GLOBAL);
|
||||
--level;
|
||||
}
|
||||
Symbol install(const char *name, Table *tpp, int level, int arena) {
|
||||
Table tp = *tpp;
|
||||
struct entry *p;
|
||||
unsigned h = (unsigned long)name&(HASHSIZE-1);
|
||||
|
||||
assert(level == 0 || level >= tp->level);
|
||||
if (level > 0 && tp->level < level)
|
||||
tp = *tpp = table(tp, level);
|
||||
NEW0(p, arena);
|
||||
p->sym.name = (char *)name;
|
||||
p->sym.scope = level;
|
||||
p->sym.up = tp->all;
|
||||
tp->all = &p->sym;
|
||||
p->link = tp->buckets[h];
|
||||
tp->buckets[h] = p;
|
||||
return &p->sym;
|
||||
}
|
||||
Symbol relocate(const char *name, Table src, Table dst) {
|
||||
struct entry *p, **q;
|
||||
Symbol *r;
|
||||
unsigned h = (unsigned long)name&(HASHSIZE-1);
|
||||
|
||||
for (q = &src->buckets[h]; *q; q = &(*q)->link)
|
||||
if (name == (*q)->sym.name)
|
||||
break;
|
||||
assert(*q);
|
||||
/*
|
||||
Remove the entry from src's hash chain
|
||||
and from its list of all symbols.
|
||||
*/
|
||||
p = *q;
|
||||
*q = (*q)->link;
|
||||
for (r = &src->all; *r && *r != &p->sym; r = &(*r)->up)
|
||||
;
|
||||
assert(*r == &p->sym);
|
||||
*r = p->sym.up;
|
||||
/*
|
||||
Insert the entry into dst's hash chain
|
||||
and into its list of all symbols.
|
||||
Return the symbol-table entry.
|
||||
*/
|
||||
p->link = dst->buckets[h];
|
||||
dst->buckets[h] = p;
|
||||
p->sym.up = dst->all;
|
||||
dst->all = &p->sym;
|
||||
return &p->sym;
|
||||
}
|
||||
Symbol lookup(const char *name, Table tp) {
|
||||
struct entry *p;
|
||||
unsigned h = (unsigned long)name&(HASHSIZE-1);
|
||||
|
||||
assert(tp);
|
||||
do
|
||||
for (p = tp->buckets[h]; p; p = p->link)
|
||||
if (name == p->sym.name)
|
||||
return &p->sym;
|
||||
while ((tp = tp->previous) != NULL);
|
||||
return NULL;
|
||||
}
|
||||
int genlabel(int n) {
|
||||
static int label = 1;
|
||||
|
||||
label += n;
|
||||
return label - n;
|
||||
}
|
||||
Symbol findlabel(int lab) {
|
||||
struct entry *p;
|
||||
unsigned h = lab&(HASHSIZE-1);
|
||||
|
||||
for (p = labels->buckets[h]; p; p = p->link)
|
||||
if (lab == p->sym.u.l.label)
|
||||
return &p->sym;
|
||||
NEW0(p, FUNC);
|
||||
p->sym.name = stringd(lab);
|
||||
p->sym.scope = LABELS;
|
||||
p->sym.up = labels->all;
|
||||
labels->all = &p->sym;
|
||||
p->link = labels->buckets[h];
|
||||
labels->buckets[h] = p;
|
||||
p->sym.generated = 1;
|
||||
p->sym.u.l.label = lab;
|
||||
(*IR->defsymbol)(&p->sym);
|
||||
return &p->sym;
|
||||
}
|
||||
Symbol constant(Type ty, Value v) {
|
||||
struct entry *p;
|
||||
unsigned h = v.u&(HASHSIZE-1);
|
||||
static union { int x; char endian; } little = { 1 };
|
||||
|
||||
ty = unqual(ty);
|
||||
for (p = constants->buckets[h]; p; p = p->link)
|
||||
if (eqtype(ty, p->sym.type, 1))
|
||||
switch (ty->op) {
|
||||
case INT: if (equalp(i)) return &p->sym; break;
|
||||
case UNSIGNED: if (equalp(u)) return &p->sym; break;
|
||||
case FLOAT:
|
||||
if (v.d == 0.0) {
|
||||
float z1 = v.d, z2 = p->sym.u.c.v.d;
|
||||
char *b1 = (char *)&z1, *b2 = (char *)&z2;
|
||||
if (z1 == z2
|
||||
&& ((!little.endian && b1[0] == b2[0])
|
||||
|| (little.endian && b1[sizeof (z1)-1] == b2[sizeof (z2)-1])))
|
||||
return &p->sym;
|
||||
} else if (equalp(d))
|
||||
return &p->sym;
|
||||
break;
|
||||
case FUNCTION: if (equalp(g)) return &p->sym; break;
|
||||
case ARRAY:
|
||||
case POINTER: if (equalp(p)) return &p->sym; break;
|
||||
default: assert(0);
|
||||
}
|
||||
NEW0(p, PERM);
|
||||
p->sym.name = vtoa(ty, v);
|
||||
p->sym.scope = CONSTANTS;
|
||||
p->sym.type = ty;
|
||||
p->sym.sclass = STATIC;
|
||||
p->sym.u.c.v = v;
|
||||
p->link = constants->buckets[h];
|
||||
p->sym.up = constants->all;
|
||||
constants->all = &p->sym;
|
||||
constants->buckets[h] = p;
|
||||
if (ty->u.sym && !ty->u.sym->addressed)
|
||||
(*IR->defsymbol)(&p->sym);
|
||||
p->sym.defined = 1;
|
||||
return &p->sym;
|
||||
}
|
||||
Symbol intconst(int n) {
|
||||
Value v;
|
||||
|
||||
v.i = n;
|
||||
return constant(inttype, v);
|
||||
}
|
||||
Symbol genident(int scls, Type ty, int lev) {
|
||||
Symbol p;
|
||||
|
||||
NEW0(p, lev >= LOCAL ? FUNC : PERM);
|
||||
p->name = stringd(genlabel(1));
|
||||
p->scope = lev;
|
||||
p->sclass = scls;
|
||||
p->type = ty;
|
||||
p->generated = 1;
|
||||
if (lev == GLOBAL)
|
||||
(*IR->defsymbol)(p);
|
||||
return p;
|
||||
}
|
||||
|
||||
Symbol temporary(int scls, Type ty) {
|
||||
Symbol p;
|
||||
|
||||
NEW0(p, FUNC);
|
||||
p->name = stringd(++tempid);
|
||||
p->scope = level < LOCAL ? LOCAL : level;
|
||||
p->sclass = scls;
|
||||
p->type = ty;
|
||||
p->temporary = 1;
|
||||
p->generated = 1;
|
||||
return p;
|
||||
}
|
||||
Symbol newtemp(int sclass, int tc, int size) {
|
||||
Symbol p = temporary(sclass, btot(tc, size));
|
||||
|
||||
(*IR->local)(p);
|
||||
p->defined = 1;
|
||||
return p;
|
||||
}
|
||||
|
||||
Symbol allsymbols(Table tp) {
|
||||
return tp->all;
|
||||
}
|
||||
|
||||
void locus(Table tp, Coordinate *cp) {
|
||||
loci = append(cp, loci);
|
||||
symbols = append(allsymbols(tp), symbols);
|
||||
}
|
||||
|
||||
void use(Symbol p, Coordinate src) {
|
||||
Coordinate *cp;
|
||||
|
||||
NEW(cp, PERM);
|
||||
*cp = src;
|
||||
p->uses = append(cp, p->uses);
|
||||
}
|
||||
/* findtype - find type ty in identifiers */
|
||||
Symbol findtype(Type ty) {
|
||||
Table tp = identifiers;
|
||||
int i;
|
||||
struct entry *p;
|
||||
|
||||
assert(tp);
|
||||
do
|
||||
for (i = 0; i < HASHSIZE; i++)
|
||||
for (p = tp->buckets[i]; p; p = p->link)
|
||||
if (p->sym.type == ty && p->sym.sclass == TYPEDEF)
|
||||
return &p->sym;
|
||||
while ((tp = tp->previous) != NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* mkstr - make a string constant */
|
||||
Symbol mkstr(char *str) {
|
||||
Value v;
|
||||
Symbol p;
|
||||
|
||||
v.p = str;
|
||||
p = constant(array(chartype, strlen(v.p) + 1, 0), v);
|
||||
if (p->u.c.loc == NULL)
|
||||
p->u.c.loc = genident(STATIC, p->type, GLOBAL);
|
||||
return p;
|
||||
}
|
||||
|
||||
/* mksymbol - make a symbol for name, install in &globals if sclass==EXTERN */
|
||||
Symbol mksymbol(int sclass, const char *name, Type ty) {
|
||||
Symbol p;
|
||||
|
||||
if (sclass == EXTERN)
|
||||
p = install(string(name), &globals, GLOBAL, PERM);
|
||||
else {
|
||||
NEW0(p, PERM);
|
||||
p->name = string(name);
|
||||
p->scope = GLOBAL;
|
||||
}
|
||||
p->sclass = sclass;
|
||||
p->type = ty;
|
||||
(*IR->defsymbol)(p);
|
||||
p->defined = 1;
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* vtoa - return string for the constant v of type ty
|
||||
*/
|
||||
char *vtoa(Type ty, Value v)
|
||||
{
|
||||
ty = unqual(ty);
|
||||
switch (ty->op) {
|
||||
case INT: return stringd(v.i);
|
||||
case UNSIGNED: return stringf((v.u&~0x7FFF) ? "0x%X" : "%U", v.u);
|
||||
case FLOAT: return stringf("%g", (double)v.d);
|
||||
case ARRAY:
|
||||
if (ty->type == chartype || ty->type == signedchar
|
||||
|| ty->type == unsignedchar)
|
||||
return v.p;
|
||||
return stringf("%p", v.p);
|
||||
case POINTER: return stringf("%p", v.p);
|
||||
case FUNCTION: return stringf("%p", v.g);
|
||||
}
|
||||
assert(0);
|
||||
return NULL;
|
||||
}
|
||||
524
src/cmd/lccom-1/symbolic.c
Normal file
524
src/cmd/lccom-1/symbolic.c
Normal file
@@ -0,0 +1,524 @@
|
||||
#include <time.h>
|
||||
#include <ctype.h>
|
||||
#include "c.h"
|
||||
|
||||
#define I(f) s_##f
|
||||
|
||||
static Node *tail;
|
||||
static int off, maxoff, uid = 0, verbose = 0, html = 0;
|
||||
|
||||
static const char *yyBEGIN(const char *tag) {
|
||||
if (html)
|
||||
print("<%s>", tag);
|
||||
return tag;
|
||||
}
|
||||
|
||||
static void yyEND(const char *tag) {
|
||||
if (html)
|
||||
print("</%s>", tag);
|
||||
if (isupper(*tag))
|
||||
print("\n");
|
||||
}
|
||||
|
||||
#define BEGIN(tag) do { const char *yytag=yyBEGIN(#tag)
|
||||
#define END yyEND(yytag); } while (0)
|
||||
#define ITEM BEGIN(li)
|
||||
#define START BEGIN(LI)
|
||||
#define ANCHOR(attr,code) do { const char *yytag="a"; if (html) { printf("<a " #attr "=\""); code; print("\">"); }
|
||||
#define NEWLINE print(html ? "<br>\n" : "\n")
|
||||
|
||||
static void emitCoord(Coordinate src) {
|
||||
if (src.file && *src.file) {
|
||||
ANCHOR(href,print("%s", src.file)); print("%s", src.file); END;
|
||||
print(":");
|
||||
}
|
||||
print("%d.%d", src.y, src.x);
|
||||
}
|
||||
|
||||
static void emitString(int len, const char *s) {
|
||||
for ( ; len-- > 0; s++)
|
||||
if (*s == '&' && html)
|
||||
print("&");
|
||||
else if (*s == '<' && html)
|
||||
print("<");
|
||||
else if (*s == '>' && html)
|
||||
print("<");
|
||||
else if (*s == '"' || *s == '\\')
|
||||
print("\\%c", *s);
|
||||
else if (*s >= ' ' && *s < 0177)
|
||||
print("%c", *s);
|
||||
else
|
||||
print("\\%d%d%d", (*s>>6)&3, (*s>>3)&7, *s&7);
|
||||
}
|
||||
|
||||
static void emitSymRef(Symbol p) {
|
||||
(*IR->defsymbol)(p);
|
||||
ANCHOR(href,print("#%s", p->x.name)); BEGIN(code); print("%s", p->name); END; END;
|
||||
}
|
||||
|
||||
static void emitSymbol(Symbol p) {
|
||||
(*IR->defsymbol)(p);
|
||||
ANCHOR(name,print("%s", p->x.name)); BEGIN(code); print("%s", p->name); END; END;
|
||||
BEGIN(ul);
|
||||
#define xx(field,code) ITEM; if (!html) print(" "); print(#field "="); code; END
|
||||
if (verbose && (src.y || src.x))
|
||||
xx(src,emitCoord(p->src));
|
||||
xx(type,print("%t", p->type));
|
||||
xx(sclass,print("%k", p->sclass));
|
||||
switch (p->scope) {
|
||||
case CONSTANTS: xx(scope,print("CONSTANTS")); break;
|
||||
case LABELS: xx(scope,print("LABELS")); break;
|
||||
case GLOBAL: xx(scope,print("GLOBAL")); break;
|
||||
case PARAM: xx(scope,print("PARAM")); break;
|
||||
case LOCAL: xx(scope,print("LOCAL")); break;
|
||||
default:
|
||||
if (p->scope > LOCAL)
|
||||
xx(scope,print("LOCAL+%d", p->scope-LOCAL));
|
||||
else
|
||||
xx(scope,print("%d", p->scope));
|
||||
}
|
||||
ITEM;
|
||||
int n = 0;
|
||||
if (!html)
|
||||
print(" ");
|
||||
print("flags=");
|
||||
#define yy(f) if (p->f) { if (n++) print("|"); print(#f); }
|
||||
yy(structarg)
|
||||
yy(addressed)
|
||||
yy(computed)
|
||||
yy(temporary)
|
||||
yy(generated)
|
||||
#undef yy
|
||||
if (n == 0)
|
||||
print("0");
|
||||
END;
|
||||
if (p->scope >= PARAM && p->sclass != STATIC)
|
||||
xx(offset,print("%d", p->x.offset));
|
||||
xx(ref,print("%f", p->ref));
|
||||
if (p->temporary && p->u.t.cse)
|
||||
xx(u.t.cse,print("%p", p->u.t.cse));
|
||||
END;
|
||||
#undef xx
|
||||
}
|
||||
|
||||
/* address - initialize q for addressing expression p+n */
|
||||
static void I(address)(Symbol q, Symbol p, long n) {
|
||||
q->name = stringf("%s%s%D", p->name, n > 0 ? "+" : "", n);
|
||||
(*IR->defsymbol)(q);
|
||||
START; print("address "); emitSymbol(q); END;
|
||||
}
|
||||
|
||||
/* blockbeg - start a block */
|
||||
static void I(blockbeg)(Env *e) {
|
||||
e->offset = off;
|
||||
START; print("blockbeg off=%d", off); END;
|
||||
}
|
||||
|
||||
/* blockend - start a block */
|
||||
static void I(blockend)(Env *e) {
|
||||
if (off > maxoff)
|
||||
maxoff = off;
|
||||
START; print("blockend off=%d", off); END;
|
||||
off = e->offset;
|
||||
}
|
||||
|
||||
/* defaddress - initialize an address */
|
||||
static void I(defaddress)(Symbol p){
|
||||
START; print("defaddress "); emitSymRef(p); END;
|
||||
}
|
||||
|
||||
/* defconst - define a constant */
|
||||
static void I(defconst)(int suffix, int size, Value v) {
|
||||
START;
|
||||
print("defconst ");
|
||||
switch (suffix) {
|
||||
case I:
|
||||
print("int.%d ", size);
|
||||
BEGIN(code);
|
||||
if (size > sizeof (int))
|
||||
print("%D", v.i);
|
||||
else
|
||||
print("%d", (int)v.i);
|
||||
END;
|
||||
break;
|
||||
case U:
|
||||
print("unsigned.%d ", size);
|
||||
BEGIN(code);
|
||||
if (size > sizeof (unsigned))
|
||||
print("%U", v.u);
|
||||
else
|
||||
print("%u", (unsigned)v.u);
|
||||
END;
|
||||
break;
|
||||
case P: print("void*.%d ", size); BEGIN(code); print("%p", v.p); END; break;
|
||||
case F:
|
||||
print("float.%d ", size);
|
||||
BEGIN(code);
|
||||
double d = v.d;
|
||||
if (d == 0.0) {
|
||||
static union { int x; char endian; } little = { 1 };
|
||||
signed char *b = (signed char *)&d;
|
||||
if ((!little.endian && b[0] < 0)
|
||||
|| (little.endian && b[sizeof (d)-1] < 0))
|
||||
print("-0.0");
|
||||
else
|
||||
print("0.0");
|
||||
} else
|
||||
print("%g", d);
|
||||
END;
|
||||
break;
|
||||
default: assert(0);
|
||||
}
|
||||
END;
|
||||
}
|
||||
|
||||
/* defstring - emit a string constant */
|
||||
static void I(defstring)(int len, char *s) {
|
||||
START; print("defstring ");
|
||||
BEGIN(code); print("\""); emitString(len, s); print("\""); END;
|
||||
END;
|
||||
}
|
||||
|
||||
/* defsymbol - define a symbol: initialize p->x */
|
||||
static void I(defsymbol)(Symbol p) {
|
||||
if (p->x.name == NULL)
|
||||
p->x.name = stringd(++uid);
|
||||
}
|
||||
|
||||
/* emit - emit the dags on list p */
|
||||
static void I(emit)(Node p){
|
||||
ITEM;
|
||||
if (!html)
|
||||
print(" ");
|
||||
for (; p; p = p->x.next) {
|
||||
if (p->op == LABEL+V) {
|
||||
assert(p->syms[0]);
|
||||
ANCHOR(name,print("%s", p->syms[0]->x.name));
|
||||
BEGIN(code); print("%s", p->syms[0]->name); END;
|
||||
END;
|
||||
print(":");
|
||||
} else {
|
||||
int i;
|
||||
if (p->x.listed) {
|
||||
BEGIN(strong); print("%d", p->x.inst); END; print("'");
|
||||
print(" %s", opname(p->op));
|
||||
} else
|
||||
print("%d. %s", p->x.inst, opname(p->op));
|
||||
if (p->count > 1)
|
||||
print(" count=%d", p->count);
|
||||
for (i = 0; i < NELEMS(p->kids) && p->kids[i]; i++)
|
||||
print(" #%d", p->kids[i]->x.inst);
|
||||
if (generic(p->op) == CALL && p->syms[0] && p->syms[0]->type)
|
||||
print(" {%t}", p->syms[0]->type);
|
||||
else
|
||||
for (i = 0; i < NELEMS(p->syms) && p->syms[i]; i++) {
|
||||
print(" ");
|
||||
if (p->syms[i]->scope == CONSTANTS)
|
||||
print(p->syms[i]->name);
|
||||
else
|
||||
emitSymRef(p->syms[i]);
|
||||
}
|
||||
}
|
||||
NEWLINE;
|
||||
}
|
||||
END;
|
||||
}
|
||||
|
||||
/* export - announce p as exported */
|
||||
static void I(export)(Symbol p) {
|
||||
START; print("export "); emitSymRef(p); END;
|
||||
}
|
||||
|
||||
/* function - generate code for a function */
|
||||
static void I(function)(Symbol f, Symbol caller[], Symbol callee[], int ncalls) {
|
||||
int i;
|
||||
|
||||
(*IR->defsymbol)(f);
|
||||
off = 0;
|
||||
for (i = 0; caller[i] && callee[i]; i++) {
|
||||
off = roundup(off, caller[i]->type->align);
|
||||
caller[i]->x.offset = callee[i]->x.offset = off;
|
||||
off += caller[i]->type->size;
|
||||
}
|
||||
if (!html) {
|
||||
print("function ");
|
||||
emitSymbol(f);
|
||||
print(" ncalls=%d\n", ncalls);
|
||||
for (i = 0; caller[i]; i++)
|
||||
START; print("caller "); emitSymbol(caller[i]); END;
|
||||
for (i = 0; callee[i]; i++)
|
||||
START; print("callee "); emitSymbol(callee[i]); END;
|
||||
} else {
|
||||
START;
|
||||
print("function");
|
||||
BEGIN(UL);
|
||||
#define xx(field,code) ITEM; print(#field "="); code; END
|
||||
xx(f,emitSymbol(f));
|
||||
xx(ncalls,print("%d", ncalls));
|
||||
if (caller[0]) {
|
||||
ITEM; print("caller"); BEGIN(OL);
|
||||
for (i = 0; caller[i]; i++)
|
||||
ITEM; emitSymbol(caller[i]); END;
|
||||
END; END;
|
||||
ITEM; print("callee"); BEGIN(OL);
|
||||
for (i = 0; callee[i]; i++)
|
||||
ITEM; emitSymbol(callee[i]); END;
|
||||
END; END;
|
||||
} else {
|
||||
xx(caller,BEGIN(em); print("empty"); END);
|
||||
xx(callee,BEGIN(em); print("empty"); END);
|
||||
}
|
||||
END;
|
||||
END;
|
||||
}
|
||||
maxoff = off = 0;
|
||||
gencode(caller, callee);
|
||||
if (html)
|
||||
START; print("emitcode"); BEGIN(ul); emitcode(); END; END;
|
||||
else
|
||||
emitcode();
|
||||
START; print("maxoff=%d", maxoff); END;
|
||||
#undef xx
|
||||
}
|
||||
|
||||
/* visit - generate code for *p */
|
||||
static int visit(Node p, int n) {
|
||||
if (p && p->x.inst == 0) {
|
||||
p->x.inst = ++n;
|
||||
n = visit(p->kids[0], n);
|
||||
n = visit(p->kids[1], n);
|
||||
*tail = p;
|
||||
tail = &p->x.next;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/* gen0 - generate code for the dags on list p */
|
||||
static Node I(gen)(Node p) {
|
||||
int n;
|
||||
Node nodelist;
|
||||
|
||||
tail = &nodelist;
|
||||
for (n = 0; p; p = p->link) {
|
||||
switch (generic(p->op)) { /* check for valid forest */
|
||||
case CALL:
|
||||
assert(IR->wants_dag || p->count == 0);
|
||||
break;
|
||||
case ARG:
|
||||
case ASGN: case JUMP: case LABEL: case RET:
|
||||
case EQ: case GE: case GT: case LE: case LT: case NE:
|
||||
assert(p->count == 0);
|
||||
break;
|
||||
case INDIR:
|
||||
assert(IR->wants_dag && p->count > 0);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
check(p);
|
||||
p->x.listed = 1;
|
||||
n = visit(p, n);
|
||||
}
|
||||
*tail = 0;
|
||||
return nodelist;
|
||||
}
|
||||
|
||||
/* global - announce a global */
|
||||
static void I(global)(Symbol p) {
|
||||
START; print("global "); emitSymbol(p); END;
|
||||
}
|
||||
|
||||
/* import - import a symbol */
|
||||
static void I(import)(Symbol p) {
|
||||
START; print("import "); emitSymRef(p); END;
|
||||
}
|
||||
|
||||
/* local - local variable */
|
||||
static void I(local)(Symbol p) {
|
||||
if (p->temporary)
|
||||
p->name = stringf("t%s", p->name);
|
||||
(*IR->defsymbol)(p);
|
||||
off = roundup(off, p->type->align);
|
||||
p->x.offset = off;
|
||||
off += p->type->size;
|
||||
START; print(p->temporary ? "temporary " : "local "); emitSymbol(p); END;
|
||||
}
|
||||
|
||||
/* progbeg - beginning of program */
|
||||
static void I(progbeg)(int argc, char *argv[]) {
|
||||
int i;
|
||||
|
||||
for (i = 1; i < argc; i++)
|
||||
if (strcmp(argv[i], "-v") == 0)
|
||||
verbose++;
|
||||
else if (strcmp(argv[i], "-html") == 0)
|
||||
html++;
|
||||
if (html) {
|
||||
print("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n");
|
||||
print("<html>");
|
||||
BEGIN(head);
|
||||
if (firstfile && *firstfile)
|
||||
BEGIN(title); emitString(strlen(firstfile), firstfile); END;
|
||||
print("<link rev=made href=\"mailto:drh@microsoft.com\">\n");
|
||||
END;
|
||||
print("<body>\n");
|
||||
if (firstfile && *firstfile)
|
||||
BEGIN(h1); emitString(strlen(firstfile), firstfile); END;
|
||||
BEGIN(P); BEGIN(em);
|
||||
print("Links lead from uses of identifiers and labels to their definitions.");
|
||||
END; END;
|
||||
print("<ul>\n");
|
||||
START;
|
||||
print("progbeg");
|
||||
BEGIN(ol);
|
||||
for (i = 1; i < argc; i++) {
|
||||
ITEM;
|
||||
BEGIN(code); print("\""); emitString(strlen(argv[i]), argv[i]); print("\""); END;
|
||||
END;
|
||||
}
|
||||
END;
|
||||
END;
|
||||
}
|
||||
}
|
||||
|
||||
/* progend - end of program */
|
||||
static void I(progend)(void) {
|
||||
START; print("progend"); END;
|
||||
if (html) {
|
||||
time_t t;
|
||||
print("</ul>\n");
|
||||
time(&t);
|
||||
print("<hr><address>%s</address>\n", ctime(&t));
|
||||
print("</body></html>\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* segment - switch to segment s */
|
||||
static void I(segment)(int s) {
|
||||
START; print("segment %s", &"text\0bss\0.data\0lit\0.sym\0."[5*s-5]); END;
|
||||
}
|
||||
|
||||
/* space - initialize n bytes of space */
|
||||
static void I(space)(int n) {
|
||||
START; print("space %d", n); END;
|
||||
}
|
||||
|
||||
static void I(stabblock)(int brace, int lev, Symbol *p) {}
|
||||
|
||||
/* stabend - finalize stab output */
|
||||
static void I(stabend)(Coordinate *cp, Symbol p, Coordinate **cpp, Symbol *sp, Symbol *stab) {
|
||||
int i;
|
||||
|
||||
if (p)
|
||||
emitSymRef(p);
|
||||
print("\n");
|
||||
if (cpp && sp)
|
||||
for (i = 0; cpp[i] && sp[i]; i++) {
|
||||
print("%w.%d: ", cpp[i], cpp[i]->x);
|
||||
emitSymRef(sp[i]);
|
||||
print("\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void I(stabfend)(Symbol p, int lineno) {}
|
||||
static void I(stabinit)(char *file, int argc, char *argv[]) {}
|
||||
|
||||
/* stabline - emit line number information for source coordinate *cp */
|
||||
static void I(stabline)(Coordinate *cp) {
|
||||
if (cp->file)
|
||||
print("%s:", cp->file);
|
||||
print("%d.%d:\n", cp->y, cp->x);
|
||||
}
|
||||
|
||||
static void I(stabsym)(Symbol p) {}
|
||||
static void I(stabtype)(Symbol p) {}
|
||||
|
||||
Interface symbolicIR = {
|
||||
{ 1, 1, 0 }, /* char */
|
||||
{ 2, 2, 0 }, /* short */
|
||||
{ 4, 4, 0 }, /* int */
|
||||
{ 4, 4, 0 }, /* long */
|
||||
{ 4, 4, 0 }, /* long long */
|
||||
{ 4, 4, 1 }, /* float */
|
||||
{ 8, 8, 1 }, /* double */
|
||||
{ 8, 8, 1 }, /* long double */
|
||||
{ 4, 4, 0 }, /* T* */
|
||||
{ 0, 4, 0 }, /* struct */
|
||||
0, /* little_endian */
|
||||
0, /* mulops_calls */
|
||||
0, /* wants_callb */
|
||||
1, /* wants_argb */
|
||||
1, /* left_to_right */
|
||||
1, /* wants_dag */
|
||||
0, /* unsigned_char */
|
||||
I(address),
|
||||
I(blockbeg),
|
||||
I(blockend),
|
||||
I(defaddress),
|
||||
I(defconst),
|
||||
I(defstring),
|
||||
I(defsymbol),
|
||||
I(emit),
|
||||
I(export),
|
||||
I(function),
|
||||
I(gen),
|
||||
I(global),
|
||||
I(import),
|
||||
I(local),
|
||||
I(progbeg),
|
||||
I(progend),
|
||||
I(segment),
|
||||
I(space),
|
||||
I(stabblock),
|
||||
I(stabend),
|
||||
I(stabfend),
|
||||
I(stabinit),
|
||||
I(stabline),
|
||||
I(stabsym),
|
||||
I(stabtype)
|
||||
};
|
||||
|
||||
Interface symbolic64IR = {
|
||||
{ 1, 1, 0 }, /* char */
|
||||
{ 2, 2, 0 }, /* short */
|
||||
{ 4, 4, 0 }, /* int */
|
||||
{ 8, 8, 0 }, /* long */
|
||||
{ 8, 8, 0 }, /* long long */
|
||||
{ 4, 4, 1 }, /* float */
|
||||
{ 8, 8, 1 }, /* double */
|
||||
{ 8, 8, 1 }, /* long double */
|
||||
{ 8, 8, 0 }, /* T* */
|
||||
{ 0, 1, 0 }, /* struct */
|
||||
1, /* little_endian */
|
||||
0, /* mulops_calls */
|
||||
0, /* wants_callb */
|
||||
1, /* wants_argb */
|
||||
1, /* left_to_right */
|
||||
1, /* wants_dag */
|
||||
0, /* unsigned_char */
|
||||
I(address),
|
||||
I(blockbeg),
|
||||
I(blockend),
|
||||
I(defaddress),
|
||||
I(defconst),
|
||||
I(defstring),
|
||||
I(defsymbol),
|
||||
I(emit),
|
||||
I(export),
|
||||
I(function),
|
||||
I(gen),
|
||||
I(global),
|
||||
I(import),
|
||||
I(local),
|
||||
I(progbeg),
|
||||
I(progend),
|
||||
I(segment),
|
||||
I(space),
|
||||
I(stabblock),
|
||||
I(stabend),
|
||||
I(stabfend),
|
||||
I(stabinit),
|
||||
I(stabline),
|
||||
I(stabsym),
|
||||
I(stabtype)
|
||||
};
|
||||
133
src/cmd/lccom-1/token.h
Normal file
133
src/cmd/lccom-1/token.h
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* xx(symbol, value, prec, op, optree, kind, string)
|
||||
*/
|
||||
yy(0, 0, 0, 0, 0, 0, 0)
|
||||
xx(FLOAT, 1, 0, 0, 0, CHAR, "float")
|
||||
xx(DOUBLE, 2, 0, 0, 0, CHAR, "double")
|
||||
xx(CHAR, 3, 0, 0, 0, CHAR, "char")
|
||||
xx(SHORT, 4, 0, 0, 0, CHAR, "short")
|
||||
xx(INT, 5, 0, 0, 0, CHAR, "int")
|
||||
xx(UNSIGNED, 6, 0, 0, 0, CHAR, "unsigned")
|
||||
xx(POINTER, 7, 0, 0, 0, 0, "pointer")
|
||||
xx(VOID, 8, 0, 0, 0, CHAR, "void")
|
||||
xx(STRUCT, 9, 0, 0, 0, CHAR, "struct")
|
||||
xx(UNION, 10, 0, 0, 0, CHAR, "union")
|
||||
xx(FUNCTION, 11, 0, 0, 0, 0, "function")
|
||||
xx(ARRAY, 12, 0, 0, 0, 0, "array")
|
||||
xx(ENUM, 13, 0, 0, 0, CHAR, "enum")
|
||||
xx(LONG, 14, 0, 0, 0, CHAR, "long")
|
||||
xx(CONST, 15, 0, 0, 0, CHAR, "const")
|
||||
xx(VOLATILE, 16, 0, 0, 0, CHAR, "volatile")
|
||||
yy(0, 17, 0, 0, 0, 0, 0)
|
||||
yy(0, 18, 0, 0, 0, 0, 0)
|
||||
yy(0, 19, 0, 0, 0, 0, 0)
|
||||
yy(0, 20, 0, 0, 0, 0, 0)
|
||||
yy(0, 21, 0, 0, 0, 0, 0)
|
||||
yy(0, 22, 0, 0, 0, 0, 0)
|
||||
yy(0, 23, 0, 0, 0, 0, 0)
|
||||
yy(0, 24, 0, 0, 0, 0, 0)
|
||||
yy(0, 25, 0, 0, 0, 0, 0)
|
||||
yy(0, 26, 0, 0, 0, 0, 0)
|
||||
yy(0, 27, 0, 0, 0, 0, 0)
|
||||
yy(0, 28, 0, 0, 0, 0, "long long")
|
||||
yy(0, 29, 0, 0, 0, 0, 0)
|
||||
yy(0, 30, 0, 0, 0, 0, 0)
|
||||
yy(0, 31, 0, 0, 0, 0, "const volatile")
|
||||
xx(ID, 32, 0, 0, 0, ID, "identifier")
|
||||
yy(0, 33, 0, 0, 0, ID, "!")
|
||||
xx(FCON, 34, 0, 0, 0, ID, "floating constant")
|
||||
xx(ICON, 35, 0, 0, 0, ID, "integer constant")
|
||||
xx(SCON, 36, 0, 0, 0, ID, "string constant")
|
||||
yy(0, 37, 13, MOD, bittree,'%', "%")
|
||||
yy(0, 38, 8, BAND, bittree,ID, "&")
|
||||
xx(INCR, 39, 0, ADD, addtree,ID, "++")
|
||||
yy(0, 40, 0, 0, 0, ID, "(")
|
||||
yy(0, 41, 0, 0, 0, ')', ")")
|
||||
yy(0, 42, 13, MUL, multree,ID, "*")
|
||||
yy(0, 43, 12, ADD, addtree,ID, "+")
|
||||
yy(0, 44, 1, 0, 0, ',', ",")
|
||||
yy(0, 45, 12, SUB, subtree,ID, "-")
|
||||
yy(0, 46, 0, 0, 0, '.', ".")
|
||||
yy(0, 47, 13, DIV, multree,'/', "/")
|
||||
xx(DECR, 48, 0, SUB, subtree,ID, "--")
|
||||
xx(DEREF, 49, 0, 0, 0, DEREF, "->")
|
||||
xx(ANDAND, 50, 5, AND, andtree,ANDAND, "&&")
|
||||
xx(OROR, 51, 4, OR, andtree,OROR, "||")
|
||||
xx(LEQ, 52, 10, LE, cmptree,LEQ, "<=")
|
||||
xx(EQL, 53, 9, EQ, eqtree, EQL, "==")
|
||||
xx(NEQ, 54, 9, NE, eqtree, NEQ, "!=")
|
||||
xx(GEQ, 55, 10, GE, cmptree,GEQ, ">=")
|
||||
xx(RSHIFT, 56, 11, RSH, shtree, RSHIFT, ">>")
|
||||
xx(LSHIFT, 57, 11, LSH, shtree, LSHIFT, "<<")
|
||||
yy(0, 58, 0, 0, 0, ':', ":")
|
||||
yy(0, 59, 0, 0, 0, IF, ";")
|
||||
yy(0, 60, 10, LT, cmptree,'<', "<")
|
||||
yy(0, 61, 2, ASGN, asgntree,'=', "=")
|
||||
yy(0, 62, 10, GT, cmptree,'>', ">")
|
||||
yy(0, 63, 0, 0, 0, '?', "?")
|
||||
xx(ELLIPSIS, 64, 0, 0, 0, ELLIPSIS,"...")
|
||||
xx(SIZEOF, 65, 0, 0, 0, ID, "sizeof")
|
||||
yy(0, 66, 0, 0, 0, 0, 0)
|
||||
xx(AUTO, 67, 0, 0, 0, STATIC, "auto")
|
||||
xx(BREAK, 68, 0, 0, 0, IF, "break")
|
||||
xx(CASE, 69, 0, 0, 0, IF, "case")
|
||||
xx(CONTINUE, 70, 0, 0, 0, IF, "continue")
|
||||
xx(DEFAULT, 71, 0, 0, 0, IF, "default")
|
||||
xx(DO, 72, 0, 0, 0, IF, "do")
|
||||
xx(ELSE, 73, 0, 0, 0, IF, "else")
|
||||
xx(EXTERN, 74, 0, 0, 0, STATIC, "extern")
|
||||
xx(FOR, 75, 0, 0, 0, IF, "for")
|
||||
xx(GOTO, 76, 0, 0, 0, IF, "goto")
|
||||
xx(IF, 77, 0, 0, 0, IF, "if")
|
||||
xx(REGISTER, 78, 0, 0, 0, STATIC, "register")
|
||||
xx(RETURN, 79, 0, 0, 0, IF, "return")
|
||||
xx(SIGNED, 80, 0, 0, 0, CHAR, "signed")
|
||||
xx(STATIC, 81, 0, 0, 0, STATIC, "static")
|
||||
xx(SWITCH, 82, 0, 0, 0, IF, "switch")
|
||||
xx(TYPEDEF, 83, 0, 0, 0, STATIC, "typedef")
|
||||
xx(WHILE, 84, 0, 0, 0, IF, "while")
|
||||
xx(TYPECODE, 85, 0, 0, 0, ID, "__typecode")
|
||||
xx(FIRSTARG, 86, 0, 0, 0, ID, "__firstarg")
|
||||
yy(0, 87, 0, 0, 0, 0, 0)
|
||||
yy(0, 88, 0, 0, 0, 0, 0)
|
||||
yy(0, 89, 0, 0, 0, 0, 0)
|
||||
yy(0, 90, 0, 0, 0, 0, 0)
|
||||
yy(0, 91, 0, 0, 0, '[', "[")
|
||||
yy(0, 92, 0, 0, 0, 0, 0)
|
||||
yy(0, 93, 0, 0, 0, ']', "]")
|
||||
yy(0, 94, 7, BXOR, bittree,'^', "^")
|
||||
yy(0, 95, 0, 0, 0, 0, 0)
|
||||
yy(0, 96, 0, 0, 0, 0, 0)
|
||||
yy(0, 97, 0, 0, 0, 0, 0)
|
||||
yy(0, 98, 0, 0, 0, 0, 0)
|
||||
yy(0, 99, 0, 0, 0, 0, 0)
|
||||
yy(0, 100, 0, 0, 0, 0, 0)
|
||||
yy(0, 101, 0, 0, 0, 0, 0)
|
||||
yy(0, 102, 0, 0, 0, 0, 0)
|
||||
yy(0, 103, 0, 0, 0, 0, 0)
|
||||
yy(0, 104, 0, 0, 0, 0, 0)
|
||||
yy(0, 105, 0, 0, 0, 0, 0)
|
||||
yy(0, 106, 0, 0, 0, 0, 0)
|
||||
yy(0, 107, 0, 0, 0, 0, 0)
|
||||
yy(0, 108, 0, 0, 0, 0, 0)
|
||||
yy(0, 109, 0, 0, 0, 0, 0)
|
||||
yy(0, 110, 0, 0, 0, 0, 0)
|
||||
yy(0, 111, 0, 0, 0, 0, 0)
|
||||
yy(0, 112, 0, 0, 0, 0, 0)
|
||||
yy(0, 113, 0, 0, 0, 0, 0)
|
||||
yy(0, 114, 0, 0, 0, 0, 0)
|
||||
yy(0, 115, 0, 0, 0, 0, 0)
|
||||
yy(0, 116, 0, 0, 0, 0, 0)
|
||||
yy(0, 117, 0, 0, 0, 0, 0)
|
||||
yy(0, 118, 0, 0, 0, 0, 0)
|
||||
yy(0, 119, 0, 0, 0, 0, 0)
|
||||
yy(0, 120, 0, 0, 0, 0, 0)
|
||||
yy(0, 121, 0, 0, 0, 0, 0)
|
||||
yy(0, 122, 0, 0, 0, 0, 0)
|
||||
yy(0, 123, 0, 0, 0, IF, "{")
|
||||
yy(0, 124, 6, BOR, bittree,'|', "|")
|
||||
yy(0, 125, 0, 0, 0, '}', "}")
|
||||
yy(0, 126, 0, BCOM, 0, ID, "~")
|
||||
xx(EOI, 127, 0, 0, 0, EOI, "end of input")
|
||||
#undef xx
|
||||
#undef yy
|
||||
169
src/cmd/lccom-1/trace.c
Normal file
169
src/cmd/lccom-1/trace.c
Normal file
@@ -0,0 +1,169 @@
|
||||
#include "c.h"
|
||||
|
||||
static char *fmt, *fp, *fmtend; /* format string, current & limit pointer */
|
||||
static Tree args; /* printf arguments */
|
||||
static Symbol frameno; /* local holding frame number */
|
||||
|
||||
/* appendstr - append str to the evolving format string, expanding it if necessary */
|
||||
static void appendstr(char *str) {
|
||||
do {
|
||||
if (fp == fmtend) {
|
||||
if (fp) {
|
||||
char *s = allocate(2*(fmtend - fmt), FUNC);
|
||||
strncpy(s, fmt, fmtend - fmt);
|
||||
fp = s + (fmtend - fmt);
|
||||
fmtend = s + 2*(fmtend - fmt);
|
||||
fmt = s;
|
||||
} else {
|
||||
fp = fmt = allocate(80, FUNC);
|
||||
fmtend = fmt + 80;
|
||||
}
|
||||
}
|
||||
} while ((*fp++ = *str++) != 0);
|
||||
fp--;
|
||||
}
|
||||
|
||||
/* tracevalue - append format and argument to print the value of e */
|
||||
static void tracevalue(Tree e, int lev) {
|
||||
Type ty = unqual(e->type);
|
||||
|
||||
switch (ty->op) {
|
||||
case INT:
|
||||
if (ty == chartype || ty == signedchar)
|
||||
appendstr("'\\x%02x'");
|
||||
else if (ty == longtype)
|
||||
appendstr("0x%ld");
|
||||
else
|
||||
appendstr("0x%d");
|
||||
break;
|
||||
case UNSIGNED:
|
||||
if (ty == chartype || ty == unsignedchar)
|
||||
appendstr("'\\x%02x'");
|
||||
else if (ty == unsignedlong)
|
||||
appendstr("0x%lx");
|
||||
else
|
||||
appendstr("0x%x");
|
||||
break;
|
||||
case FLOAT:
|
||||
if (ty == longdouble)
|
||||
appendstr("%Lg");
|
||||
else
|
||||
appendstr("%g");
|
||||
break;
|
||||
case POINTER:
|
||||
if (unqual(ty->type) == chartype
|
||||
|| unqual(ty->type) == signedchar
|
||||
|| unqual(ty->type) == unsignedchar) {
|
||||
static Symbol null;
|
||||
if (null == NULL)
|
||||
null = mkstr("(null)");
|
||||
tracevalue(cast(e, unsignedtype), lev + 1);
|
||||
appendstr(" \"%.30s\"");
|
||||
e = condtree(e, e, pointer(idtree(null->u.c.loc)));
|
||||
} else {
|
||||
appendstr("("); appendstr(typestring(ty, "")); appendstr(")0x%x");
|
||||
}
|
||||
break;
|
||||
case STRUCT: {
|
||||
Field q;
|
||||
appendstr("("); appendstr(typestring(ty, "")); appendstr("){");
|
||||
for (q = ty->u.sym->u.s.flist; q; q = q->link) {
|
||||
appendstr(q->name); appendstr("=");
|
||||
tracevalue(field(addrof(e), q->name), lev + 1);
|
||||
if (q->link)
|
||||
appendstr(",");
|
||||
}
|
||||
appendstr("}");
|
||||
return;
|
||||
}
|
||||
case UNION:
|
||||
appendstr("("); appendstr(typestring(ty, "")); appendstr("){...}");
|
||||
return;
|
||||
case ARRAY:
|
||||
if (lev && ty->type->size > 0) {
|
||||
int i;
|
||||
e = pointer(e);
|
||||
appendstr("{");
|
||||
for (i = 0; i < ty->size/ty->type->size; i++) {
|
||||
Tree p = (*optree['+'])(ADD, e, consttree(i, inttype));
|
||||
if (isptr(p->type) && isarray(p->type->type))
|
||||
p = retype(p, p->type->type);
|
||||
else
|
||||
p = rvalue(p);
|
||||
if (i)
|
||||
appendstr(",");
|
||||
tracevalue(p, lev + 1);
|
||||
}
|
||||
appendstr("}");
|
||||
} else
|
||||
appendstr(typestring(ty, ""));
|
||||
return;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
e = cast(e, promote(ty));
|
||||
args = tree(mkop(ARG,e->type), e->type, e, args);
|
||||
}
|
||||
|
||||
/* tracefinis - complete & generate the trace call to print */
|
||||
static void tracefinis(Symbol printer) {
|
||||
Tree *ap;
|
||||
Symbol p;
|
||||
|
||||
*fp = 0;
|
||||
p = mkstr(string(fmt));
|
||||
for (ap = &args; *ap; ap = &(*ap)->kids[1])
|
||||
;
|
||||
*ap = tree(ARG+P, charptype, pointer(idtree(p->u.c.loc)), 0);
|
||||
walk(calltree(pointer(idtree(printer)), freturn(printer->type), args, NULL), 0, 0);
|
||||
args = 0;
|
||||
fp = fmtend = 0;
|
||||
}
|
||||
|
||||
/* tracecall - generate code to trace entry to f */
|
||||
static void tracecall(Symbol printer, Symbol f, void *ignore) {
|
||||
int i;
|
||||
Symbol counter = genident(STATIC, inttype, GLOBAL);
|
||||
|
||||
defglobal(counter, BSS);
|
||||
(*IR->space)(counter->type->size);
|
||||
frameno = genident(AUTO, inttype, level);
|
||||
addlocal(frameno);
|
||||
appendstr(f->name); appendstr("#");
|
||||
tracevalue(asgn(frameno, incr(INCR, idtree(counter), consttree(1, inttype))), 0);
|
||||
appendstr("(");
|
||||
for (i = 0; f->u.f.callee[i]; i++) {
|
||||
if (i)
|
||||
appendstr(",");
|
||||
appendstr(f->u.f.callee[i]->name); appendstr("=");
|
||||
tracevalue(idtree(f->u.f.callee[i]), 0);
|
||||
}
|
||||
if (variadic(f->type))
|
||||
appendstr(",...");
|
||||
appendstr(") called\n");
|
||||
tracefinis(printer);
|
||||
}
|
||||
|
||||
/* tracereturn - generate code to trace return e */
|
||||
static void tracereturn(Symbol printer, Symbol f, Tree e) {
|
||||
appendstr(f->name); appendstr("#");
|
||||
tracevalue(idtree(frameno), 0);
|
||||
appendstr(" returned");
|
||||
if (freturn(f->type) != voidtype && e) {
|
||||
appendstr(" ");
|
||||
tracevalue(e, 0);
|
||||
}
|
||||
appendstr("\n");
|
||||
tracefinis(printer);
|
||||
}
|
||||
|
||||
/* traceInit - initialize for tracing */
|
||||
void traceInit(char *arg) {
|
||||
if (strncmp(arg, "-t", 2) == 0 && strchr(arg, '=') == NULL) {
|
||||
Symbol printer = mksymbol(EXTERN, arg[2] ? &arg[2] : "printf",
|
||||
ftype(inttype, ptr(qual(CONST, chartype)), voidtype, NULL));
|
||||
printer->defined = 0;
|
||||
attach((Apply)tracecall, printer, &events.entry);
|
||||
attach((Apply)tracereturn, printer, &events.returns);
|
||||
}
|
||||
}
|
||||
240
src/cmd/lccom-1/tree.c
Normal file
240
src/cmd/lccom-1/tree.c
Normal file
@@ -0,0 +1,240 @@
|
||||
#include "c.h"
|
||||
|
||||
int where = STMT;
|
||||
static int warn;
|
||||
static int nid = 1; /* identifies trees & nodes in debugging output */
|
||||
static struct nodeid {
|
||||
int printed;
|
||||
Tree node;
|
||||
} ids[500]; /* if ids[i].node == p, then p's id is i */
|
||||
|
||||
static void printtree1(Tree, int, int);
|
||||
|
||||
Tree tree(int op, Type type, Tree left, Tree right) {
|
||||
Tree p;
|
||||
|
||||
NEW0(p, where);
|
||||
p->op = op;
|
||||
p->type = type;
|
||||
p->kids[0] = left;
|
||||
p->kids[1] = right;
|
||||
return p;
|
||||
}
|
||||
|
||||
Tree texpr(Tree (*f)(int), int tok, int a) {
|
||||
int save = where;
|
||||
Tree p;
|
||||
|
||||
where = a;
|
||||
p = (*f)(tok);
|
||||
where = save;
|
||||
return p;
|
||||
}
|
||||
static Tree root1(Tree p) {
|
||||
if (p == NULL)
|
||||
return p;
|
||||
if (p->type == voidtype)
|
||||
warn++;
|
||||
switch (generic(p->op)) {
|
||||
case COND: {
|
||||
Tree q = p->kids[1];
|
||||
assert(q && q->op == RIGHT);
|
||||
if (p->u.sym && q->kids[0] && generic(q->kids[0]->op) == ASGN)
|
||||
q->kids[0] = root1(q->kids[0]->kids[1]);
|
||||
else
|
||||
q->kids[0] = root1(q->kids[0]);
|
||||
if (p->u.sym && q->kids[1] && generic(q->kids[1]->op) == ASGN)
|
||||
q->kids[1] = root1(q->kids[1]->kids[1]);
|
||||
else
|
||||
q->kids[1] = root1(q->kids[1]);
|
||||
p->u.sym = 0;
|
||||
if (q->kids[0] == 0 && q->kids[1] == 0)
|
||||
p = root1(p->kids[0]);
|
||||
}
|
||||
break;
|
||||
case AND: case OR:
|
||||
if ((p->kids[1] = root1(p->kids[1])) == 0)
|
||||
p = root1(p->kids[0]);
|
||||
break;
|
||||
case NOT:
|
||||
if (warn++ == 0)
|
||||
warning("expression with no effect elided\n");
|
||||
return root1(p->kids[0]);
|
||||
case RIGHT:
|
||||
if (p->kids[1] == 0)
|
||||
return root1(p->kids[0]);
|
||||
if (p->kids[0] && p->kids[0]->op == CALL+B
|
||||
&& p->kids[1] && p->kids[1]->op == INDIR+B)
|
||||
/* avoid premature release of the CALL+B temporary */
|
||||
return p->kids[0];
|
||||
if (p->kids[0] && p->kids[0]->op == RIGHT
|
||||
&& p->kids[1] == p->kids[0]->kids[0])
|
||||
/* de-construct e++ construction */
|
||||
return p->kids[0]->kids[1];
|
||||
p = tree(RIGHT, p->type, root1(p->kids[0]), root1(p->kids[1]));
|
||||
return p->kids[0] || p->kids[1] ? p : (Tree)0;
|
||||
case EQ: case NE: case GT: case GE: case LE: case LT:
|
||||
case ADD: case SUB: case MUL: case DIV: case MOD:
|
||||
case LSH: case RSH: case BAND: case BOR: case BXOR:
|
||||
if (warn++ == 0)
|
||||
warning("expression with no effect elided\n");
|
||||
p = tree(RIGHT, p->type, root1(p->kids[0]), root1(p->kids[1]));
|
||||
return p->kids[0] || p->kids[1] ? p : (Tree)0;
|
||||
case INDIR:
|
||||
if (p->type->size == 0 && unqual(p->type) != voidtype)
|
||||
warning("reference to `%t' elided\n", p->type);
|
||||
if (isptr(p->kids[0]->type) && isvolatile(p->kids[0]->type->type))
|
||||
warning("reference to `volatile %t' elided\n", p->type);
|
||||
/* fall thru */
|
||||
case NEG: case BCOM: case FIELD:
|
||||
if (warn++ == 0)
|
||||
warning("expression with no effect elided\n");
|
||||
return root1(p->kids[0]);
|
||||
case ADDRL: case ADDRG: case ADDRF: case CNST:
|
||||
if (needconst)
|
||||
return p;
|
||||
if (warn++ == 0)
|
||||
warning("expression with no effect elided\n");
|
||||
return NULL;
|
||||
case CVF:
|
||||
if (optype(p->op) == I
|
||||
|| p->type->size < p->kids[0]->type->size)
|
||||
if (warn++ == 0)
|
||||
warning("expression with no effect elided\n");
|
||||
return root1(p->kids[0]);
|
||||
case CVI:
|
||||
if ((optype(p->op) == U || optype(p->op) == I)
|
||||
&& p->type->size < p->kids[0]->type->size
|
||||
&& specific(p->kids[0]->op) != CALL+I)
|
||||
if (warn++ == 0)
|
||||
warning("expression with no effect elided\n");
|
||||
return root1(p->kids[0]);
|
||||
case CVU: case CVP:
|
||||
if ((optype(p->op) == U && p->type->size < p->kids[0]->type->size)
|
||||
|| (optype(p->op) == I && p->type->size <= p->kids[0]->type->size))
|
||||
if (warn++ == 0)
|
||||
warning("expression with no effect elided\n");
|
||||
return root1(p->kids[0]);
|
||||
case ARG: case ASGN: case CALL: case JUMP: case LABEL:
|
||||
break;
|
||||
default: assert(0);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
Tree root(Tree p) {
|
||||
warn = 0;
|
||||
return root1(p);
|
||||
}
|
||||
|
||||
char *opname(int op) {
|
||||
static char *opnames[] = {
|
||||
"",
|
||||
"CNST",
|
||||
"ARG",
|
||||
"ASGN",
|
||||
"INDIR",
|
||||
"CVC",
|
||||
"CVD",
|
||||
"CVF",
|
||||
"CVI",
|
||||
"CVP",
|
||||
"CVS",
|
||||
"CVU",
|
||||
"NEG",
|
||||
"CALL",
|
||||
"*LOAD*",
|
||||
"RET",
|
||||
"ADDRG",
|
||||
"ADDRF",
|
||||
"ADDRL",
|
||||
"ADD",
|
||||
"SUB",
|
||||
"LSH",
|
||||
"MOD",
|
||||
"RSH",
|
||||
"BAND",
|
||||
"BCOM",
|
||||
"BOR",
|
||||
"BXOR",
|
||||
"DIV",
|
||||
"MUL",
|
||||
"EQ",
|
||||
"GE",
|
||||
"GT",
|
||||
"LE",
|
||||
"LT",
|
||||
"NE",
|
||||
"JUMP",
|
||||
"LABEL",
|
||||
"AND",
|
||||
"NOT",
|
||||
"OR",
|
||||
"COND",
|
||||
"RIGHT",
|
||||
"FIELD"
|
||||
}, *suffixes[] = {
|
||||
"0", "F", "D", "C", "S", "I", "U", "P", "V", "B",
|
||||
"10","11","12","13","14","15"
|
||||
};
|
||||
|
||||
if (generic(op) >= AND && generic(op) <= FIELD && opsize(op) == 0)
|
||||
return opnames[opindex(op)];
|
||||
return stringf("%s%s%s",
|
||||
opindex(op) > 0 && opindex(op) < NELEMS(opnames) ?
|
||||
opnames[opindex(op)] : stringd(opindex(op)),
|
||||
suffixes[optype(op)], opsize(op) > 0 ? stringd(opsize(op)) : "");
|
||||
}
|
||||
|
||||
int nodeid(Tree p) {
|
||||
int i = 1;
|
||||
|
||||
ids[nid].node = p;
|
||||
while (ids[i].node != p)
|
||||
i++;
|
||||
if (i == nid)
|
||||
ids[nid++].printed = 0;
|
||||
return i;
|
||||
}
|
||||
|
||||
/* printed - return pointer to ids[id].printed */
|
||||
int *printed(int id) {
|
||||
if (id)
|
||||
return &ids[id].printed;
|
||||
nid = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* printtree - print tree p on fd */
|
||||
void printtree(Tree p, int fd) {
|
||||
(void)printed(0);
|
||||
printtree1(p, fd, 1);
|
||||
}
|
||||
|
||||
/* printtree1 - recursively print tree p */
|
||||
static void printtree1(Tree p, int fd, int lev) {
|
||||
FILE *f = fd == 1 ? stdout : stderr;
|
||||
int i;
|
||||
static char blanks[] = " ";
|
||||
|
||||
if (p == 0 || *printed(i = nodeid(p)))
|
||||
return;
|
||||
fprint(f, "#%d%S%S", i, blanks, i < 10 ? 2 : i < 100 ? 1 : 0, blanks, lev);
|
||||
fprint(f, "%s %t", opname(p->op), p->type);
|
||||
*printed(i) = 1;
|
||||
for (i = 0; i < NELEMS(p->kids); i++)
|
||||
if (p->kids[i])
|
||||
fprint(f, " #%d", nodeid(p->kids[i]));
|
||||
if (p->op == FIELD && p->u.field)
|
||||
fprint(f, " %s %d..%d", p->u.field->name,
|
||||
fieldsize(p->u.field) + fieldright(p->u.field), fieldright(p->u.field));
|
||||
else if (generic(p->op) == CNST)
|
||||
fprint(f, " %s", vtoa(p->type, p->u.v));
|
||||
else if (p->u.sym)
|
||||
fprint(f, " %s", p->u.sym->name);
|
||||
if (p->node)
|
||||
fprint(f, " node=%p", p->node);
|
||||
fprint(f, "\n");
|
||||
for (i = 0; i < NELEMS(p->kids); i++)
|
||||
printtree1(p->kids[i], fd, lev + 1);
|
||||
}
|
||||
39
src/cmd/lccom-1/tst/8q.c
Normal file
39
src/cmd/lccom-1/tst/8q.c
Normal file
@@ -0,0 +1,39 @@
|
||||
int up[15], down[15], rows[8], x[8];
|
||||
int queens(), print();
|
||||
|
||||
main()
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 15; i++)
|
||||
up[i] = down[i] = 1;
|
||||
for (i = 0; i < 8; i++)
|
||||
rows[i] = 1;
|
||||
queens(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
queens(c)
|
||||
{
|
||||
int r;
|
||||
|
||||
for (r = 0; r < 8; r++)
|
||||
if (rows[r] && up[r-c+7] && down[r+c]) {
|
||||
rows[r] = up[r-c+7] = down[r+c] = 0;
|
||||
x[c] = r;
|
||||
if (c == 7)
|
||||
print();
|
||||
else
|
||||
queens(c + 1);
|
||||
rows[r] = up[r-c+7] = down[r+c] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
print()
|
||||
{
|
||||
int k;
|
||||
|
||||
for (k = 0; k < 8; k++)
|
||||
printf("%c ", x[k]+'1');
|
||||
printf("\n");
|
||||
}
|
||||
48
src/cmd/lccom-1/tst/array.c
Normal file
48
src/cmd/lccom-1/tst/array.c
Normal file
@@ -0,0 +1,48 @@
|
||||
int x[3][4], *y[3];
|
||||
|
||||
main() {
|
||||
int z[3][4];
|
||||
int i, j, *p;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
for (j = 0; j < 4; j++)
|
||||
x[i][j] = 1000*i + j;
|
||||
y[i] = x[i];
|
||||
}
|
||||
f();
|
||||
for (i = 0; i < 3; i++) {
|
||||
y[i] = p = &z[i][0];
|
||||
for (j = 0; j < 4; j++)
|
||||
p[j] = x[i][j];
|
||||
}
|
||||
g(z, y);
|
||||
return 0;
|
||||
}
|
||||
|
||||
f() {
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
for (j = 0; j < 4; j++)
|
||||
printf(" %d", x[i][j]);
|
||||
printf("\n");
|
||||
for (i = 0; i < 3; i++)
|
||||
for (j = 0; j < 4; j++)
|
||||
printf(" %d", y[i][j]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
g(x, y)
|
||||
int x[][4], *y[];
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
for (j = 0; j < 4; j++)
|
||||
printf(" %d", x[i][j]);
|
||||
printf("\n");
|
||||
for (i = 0; i < 3; i++)
|
||||
for (j = 0; j < 4; j++)
|
||||
printf(" %d", y[i][j]);
|
||||
printf("\n");
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user