1 Commits

Author SHA1 Message Date
Sergey
163dcbd961 Branch "uflash" imported from GoogleCode. 2015-03-21 14:11:41 -07:00
457 changed files with 146252 additions and 4193 deletions

View File

@@ -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

View File

@@ -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
View 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
View File

@@ -0,0 +1,6 @@
int main()
{
write(1,"hello\n",6);
exit(1);
}

100
share/lccom/sys.s Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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);
}

View File

@@ -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.
#

View File

@@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

74
src/cmd/cron/job.c Executable file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

40
src/cmd/lccom-1/bind.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

View 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
&quot;installed&quot; 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 &lt;string.h&gt;
static char rcsid[] = &quot;$ Id: solaris.c,v 1.10 1998/09/14 20:36:33 drh Exp $&quot;;
#ifndef LCCDIR
#define LCCDIR &quot;/usr/local/lib/lcc/&quot;
#endif
#ifndef SUNDIR
#define SUNDIR &quot;/opt/SUNWspro/SC4.2/lib/&quot;
#endif
char *suffixes[] = { &quot;.c&quot;, &quot;.i&quot;, &quot;.s&quot;, &quot;.o&quot;, &quot;.out&quot;, 0 };
char inputs[256] = &quot;&quot;;
char *cpp[] = { LCCDIR &quot;cpp&quot;,
&quot;-D__STDC__=1&quot;, &quot;-Dsparc&quot;, &quot;-D__sparc__&quot;, &quot;-Dsun&quot;, &quot;-D__sun__&quot;, &quot;-Dunix&quot;,
&quot;$1&quot;, &quot;$2&quot;, &quot;$3&quot;, 0 };
char *include[] = { &quot;-I&quot; LCCDIR &quot;include&quot;, &quot;-I/usr/local/include&quot;,
&quot;-I/usr/include&quot;, 0 };
char *com[] = { LCCDIR &quot;rcc&quot;, &quot;-target=sparc/solaris&quot;,
&quot;$1&quot;, &quot;$2&quot;, &quot;$3&quot;, 0 };
char *as[] = { &quot;/usr/ccs/bin/as&quot;, &quot;-Qy&quot;, &quot;-s&quot;, &quot;-o&quot;, &quot;$3&quot;, &quot;$1&quot;, &quot;$2&quot;, 0 };
char *ld[] = { &quot;/usr/ccs/bin/ld&quot;, &quot;-o&quot;, &quot;$3&quot;, &quot;$1&quot;,
SUNDIR &quot;crti.o&quot;, SUNDIR &quot;crt1.o&quot;,
SUNDIR &quot;values-xa.o&quot;, &quot;$2&quot;, &quot;&quot;,
&quot;-Y&quot;, &quot;P,&quot; SUNDIR &quot;:/usr/ccs/lib:/usr/lib&quot;, &quot;-Qy&quot;,
&quot;-L&quot; LCCDIR, &quot;-llcc&quot;, &quot;-lm&quot;, &quot;-lc&quot;, SUNDIR &quot;crtn.o&quot;, 0 };
extern char *concat(char *, char *);
int option(char *arg) {
if (strncmp(arg, &quot;-lccdir=&quot;, 8) == 0) {
cpp[0] = concat(&amp;arg[8], &quot;/cpp&quot;);
include[0] = concat(&quot;-I&quot;, concat(&amp;arg[8], &quot;/include&quot;));
ld[12] = concat(&quot;-L&quot;, &amp;arg[8]);
com[0] = concat(&amp;arg[8], &quot;/rcc&quot;);
} else if (strcmp(arg, &quot;-p&quot;) == 0) {
ld[5] = SUNDIR &quot;mcrt1.o&quot;;
ld[10] = &quot;P,&quot; SUNDIR &quot;libp:/usr/ccs/lib/libp:/usr/lib/libp:&quot;
SUNDIR &quot;:/usr/ccs/lib:/usr/lib&quot;;
} else if (strcmp(arg, &quot;-b&quot;) == 0)
;
else if (strncmp(arg, &quot;-ld=&quot;, 4) == 0)
ld[0] = &amp;arg[4];
else
return 0;
return 1;
}</pre>
</blockquote>
<p><code>LCCDIR</code> defaults to <code>&quot;/usr/local/lib/lcc/&quot;</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=\&quot;/v/lib/lcc/\&quot;' 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[] = { &quot;.c;.C&quot;, &quot;.i;.I&quot;, &quot;.asm;.ASM;.s;.S&quot;, &quot;.obj;.OBJ&quot;, &quot;.exe&quot;, 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>&quot;-Dsparc&quot;</code> in the <code>cpp</code>
command above, are passed to the command as given.</p>
<p>The strings <code>&quot;$1&quot;</code>, <code>&quot;$2&quot;</code>, and <code>&quot;$3&quot;</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>&quot;$3&quot;</code> becomes <code>&quot;&quot;</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 &quot;<code>win32</code>&quot;, 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 &quot;.o&quot; 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 &quot;<code>-target</code> <em>name</em>&quot;
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 &quot;<code>make all</code>&quot;. 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.,
&quot;<code>make CC=gcc all</code>&quot;. If you're running on a DEC ALPHA, use &quot;<code>make
CC='cc -std1' all</code>&quot;; 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., &quot;<code>$BUILDDIR/rcc</code>
is generating code for a <code>sparc</code> running the <code>solaris</code> operating
system.&quot;</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 &quot;triple test&quot;, 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 &quot;<code>make clean</code>&quot; cleans up, but does not remove <code>rcc</code>,
etc., and &quot;<code>make clobber</code>&quot; 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 &quot;symbolic&quot; 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 &quot;null&quot; 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&gt;set BUILDDIR=\progra~1\lcc\<i>version</i>\bin
C:\dist\lcc&gt;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&gt;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&gt;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&gt;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&gt;mkdir %BUILDDIR%\x86\win32\tst
C:\dist\lcc&gt;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 &quot;triple&quot; test, which compiles <code>rcc</code> with itself and
verifies the results:<blockquote>
<pre>C:\dist\lcc&gt;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&gt;copy %BUILDDIR%\lcc.exe \bin
1 file(s) copied.
C:\dist\lcc&gt;copy %BUILDDIR%\bprint.exe \bin
1 file(s) copied.</pre>
</blockquote>
</li>
<li>Finally, clean up:<blockquote>
<pre>C:\dist\lcc&gt;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.; &quot;<code>nmake -f makefile.nt clobber</code>&quot; 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&gt;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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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;
}
}

View 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);
}

View 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);
}

View 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.

View 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);
}
}

View 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
View 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];
}

View 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;
}

View 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;
}

View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

73
src/cmd/lccom-1/null.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

329
src/cmd/lccom-1/stab.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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("&amp;");
else if (*s == '<' && html)
print("&lt;");
else if (*s == '>' && html)
print("&lt;");
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
View 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
View 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
View 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
View 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");
}

View 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