Compare commits

..

8 commits
main ... 0.2.2

Author SHA1 Message Date
Wolfgang Bumiller
de24486e54 Bump to patch level 2 2012-12-27 14:44:54 +01:00
Wolfgang Bumiller
e26d1e985a fix segfault from previous cherry-pick 2012-12-27 14:43:31 +01:00
Wolfgang Bumiller
cac827d763 Actually generate the vector member ir_values in ir_function_finalize, since ir_builder_gen_global is too late / happens after life ranges; this should fix -Ooverlap-locals
Conflicts:
	ir.c
2012-12-27 14:38:47 +01:00
Wolfgang Bumiller
9a87c5aac7 manpage: -dump, -dumpfin 2012-12-27 14:37:44 +01:00
Wolfgang Bumiller
b2165f718b Another bool->int fix; now clang-compiled gmqcc actually works... 2012-12-27 14:35:25 +01:00
Wolfgang Bumiller
9348649af4 Bump version to 0.2.1 2012-12-23 21:58:14 +01:00
Wolfgang Bumiller
11ffe61314 Even safer vector macros 2012-12-23 21:51:48 +01:00
Wolfgang Bumiller
b54f0a64de Fixing vector macros 2012-12-23 21:51:45 +01:00
196 changed files with 18115 additions and 27032 deletions

15
.gitignore vendored
View file

@ -1,15 +0,0 @@
*.o
*.d
*.orig
*.rej
*.patch
*.diff
*.exe
gmqcc
gmqpak
qcvm
testsuite
build/
.idea/

13
AUTHORS
View file

@ -1,10 +1,3 @@
Authors:
Dale `graphitemaster` Weiler - Charismatic Visionary / Programmer
Wolfgang `Blub\w` Bumiller - Main Programmer
Thanks to:
Forest `LordHavoc` Hale - Technical support and assistance
Rudolf `divVerent` Polzer - Technical support and assistance
Matthias `matthiaskrgr` Krüger - Miscellaneous assistance
Samual `Samual` Lenks - Preprocessor assistance
Igor `ignatenkobrain` Gnatenko - Fedora packages
GMQCC brought to you by:
Dale Weiler
Wolfgang Bumiller

View file

@ -1,35 +0,0 @@
cmake_minimum_required(VERSION 2.8)
project(gmqcc)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
set(SOURCE_FILES
algo.h
ast.cpp ast.h
code.cpp
conout.cpp
fold.cpp fold.h
ftepp.cpp
gmqcc.h
intrin.cpp intrin.h
ir.cpp
ir.h
lexer.cpp lexer.h
opts.cpp
parser.cpp parser.h
stat.cpp
utf8.cpp
util.cpp)
add_library(gmqcclib ${SOURCE_FILES})
add_executable(gmqcc main.cpp)
target_link_libraries(gmqcc gmqcclib)
add_executable(testsuite test.cpp)
target_link_libraries(testsuite gmqcclib)
add_executable(qcvm exec.cpp)
target_link_libraries(qcvm gmqcclib)

39
INSTALL Normal file
View file

@ -0,0 +1,39 @@
Installing gmqcc
1. Prerequisites
- A C-Compiler such as gcc or clang
- GNU Make. This document will assume GNU-Make to be executed via
`make'. On BSD systems you probably have to use `gmake' instead.
2. Compilation
Run the GNU make program `make' or `gmake'.
make
If no error appears, the following binary files will have been
created:
- gmqcc
- qcvm
3. Installation
The `install' target will install the 2 binaries to /usr/local/bin
by default.
The Makefile honors the following variables:
- DESTDIR: The installation directory root.
- PREFIX: The installation prefix, default: /usr/local
- BINDIR: Directory for binary executables,
deafult: $PREFIX/bin
To install to /usr/local run:
make install
To install to /usr run:
make PREFIX=/usr install
To install to a package-staging directory such as $pkgdir when
writing an ArchLinux PKGBUILD file:
make DESTDIR=$pkgdir install

View file

@ -1,4 +1,4 @@
Copyright (C) 2012, 2013, 2014, 2015
Copyright (C) 2012
Dale Weiler
Wolfgang Bumiller

279
Makefile
View file

@ -1,204 +1,107 @@
# Compilation options:
# * LTO - Link time optimization [default=0]
# * ASAN - Address sanitizer [default=0]
# * UBSAN - Undefined behavior sanitizer [default=0]
# * DEBUG - Debug build [default=0]
# * UNUSED - Remove unused references [default=1]
# * SRCDIR - Out of tree builds [default=./]
LTO ?= 0
ASAN ?= 0
UBSAN ?= 0
DEBUG ?= 0
UNUSED ?= 1
SRCDIR ?= ./
DESTDIR :=
PREFIX := /usr/local
BINDIR := $(PREFIX)/bin
DATADIR := $(PREFIX)/share
MANDIR := $(DATADIR)/man
# Determine if we're building for Windows or not so we can set the right file
# extensions for the binaries and exclude the testsuite because it doesn't build
# for that platform.
ifeq ($(OS),Windows_NT)
GMQCC := gmqcc.exe
QCVM := qcvm.exe
UNAME ?= $(shell uname)
CYGWIN = $(findstring CYGWIN, $(UNAME))
MINGW = $(findstring MINGW32, $(UNAME))
CC ?= clang
CFLAGS += -Wall -Wextra -I. -pedantic-errors
#turn on tons of warnings if clang is present
# but also turn off the STUPID ONES
ifeq ($(CC), clang)
CFLAGS += \
-Weverything \
-Wno-padded \
-Wno-format-nonliteral \
-Wno-disabled-macro-expansion \
-Wno-conversion \
-Wno-missing-prototypes \
-Wno-float-equal \
-Wno-cast-align
endif
ifeq ($(track), no)
CFLAGS += -DNOTRACK
endif
OBJ_D = util.o code.o ast.o ir.o conout.o ftepp.o opts.o file.o
OBJ_T = test.o util.o conout.o file.o
OBJ_C = main.o lexer.o parser.o file.o
OBJ_X = exec-standalone.o util.o conout.o file.o
ifneq ("$(CYGWIN)", "")
#nullify the common variables that
#most *nix systems have (for windows)
PREFIX :=
BINDIR :=
DATADIR :=
MANDIR :=
QCVM = qcvm.exe
GMQCC = gmqcc.exe
TESTSUITE = testsuite.exe
else
GMQCC := gmqcc
QCVM := qcvm
TESTSUITE := testsuite
endif
# C++ compiler
CXX ?= clang++
# Build artifact directories
OBJDIR := .build/objs
DEPDIR := .build/deps
# Collect all the source files for GMQCC.
GSRCS := ast.cpp
GSRCS += code.cpp
GSRCS += conout.cpp
GSRCS += fold.cpp
GSRCS += ftepp.cpp
GSRCS += intrin.cpp
GSRCS += ir.cpp
GSRCS += lexer.cpp
GSRCS += main.cpp
GSRCS += opts.cpp
GSRCS += parser.cpp
GSRCS += stat.cpp
GSRCS += utf8.cpp
GSRCS += util.cpp
# Collect all the source files for QCVM.
QSRCS := exec.cpp
QSRCS += stat.cpp
QSRCS += util.cpp
# Collect all the source files for TESTSUITE.
TSRCS := conout.cpp
TSRCS += opts.cpp
TSRCS += stat.cpp
TSRCS += test.cpp
TSRCS += util.cpp
#
# Compilation flags
#
CXXFLAGS := -Wall
CXXFLAGS += -Wextra
CXXFLAGS += -Wno-parentheses
CXXFLAGS += -Wno-class-memaccess
CXXFLAGS += -Wno-implicit-fallthrough
CXXFLAGS += -std=c++11
# Disable some unneeded features.
CXXFLAGS += -fno-exceptions
CXXFLAGS += -fno-rtti
CXXFLAGS += -fno-asynchronous-unwind-tables
# Give each function and data it's own section so the linker can remove unused
# references to each, producing smaller, tighter binaries.
ifeq ($(UNUSED),1)
CXXFLAGS += -ffunction-sections
CXXFLAGS += -fdata-sections
endif
# Enable link-time optimizations if requested.
ifeq ($(LTO),1)
CXXFLAGS += -flto
endif
ifeq ($(DEBUG),1)
# Ensure there is a frame-pointer in debug builds.
CXXFLAGS += -fno-omit-frame-pointer
# Disable all optimizations in debug builds.
CXXFLAGS += -O0
# Enable debug symbols.
CXXFLAGS += -g
ifneq ("$(MINGW)", "")
#nullify the common variables that
#most *nix systems have (for windows)
PREFIX :=
BINDIR :=
DATADIR :=
MANDIR :=
QCVM = qcvm.exe
GMQCC = gmqcc.exe
TESTSUITE = testsuite.exe
else
# Disable all the stack protection features in release builds.
CXXFLAGS += -fno-stack-protector
CXXFLAGS += -fno-stack-check
# Disable frame pointer in release builds when AddressSanitizer isn't present.
ifeq ($(ASAN),1)
CXXFLAGS += -fno-omit-frame-pointer
else
CXXFLAGS += -fomit-frame-pointer
endif
# Highest optimization flag in release builds.
CXXFLAGS += -O3
QCVM = qcvm
GMQCC = gmqcc
TESTSUITE = testsuite
endif
endif
# Sanitizer selection
ifeq ($(ASAN),1)
CXXFLAGS += -fsanitize=address
endif
ifeq ($(UBSAN),1)
CXXFLAGS += -fsanitize=undefined
endif
#standard rules
default: all
%.o: %.c
$(CC) -c $< -o $@ $(CFLAGS)
#
# Dependency flags
#
DEPFLAGS := -MMD
DEPFLAGS += -MP
exec-standalone.o: exec.c
$(CC) -c $< -o $@ $(CFLAGS) -DQCVM_EXECUTOR=1
#
# Linker flags
#
LDFLAGS :=
$(QCVM): $(OBJ_X)
$(CC) -o $@ $^ $(CFLAGS) -lm
# Remove unreferenced sections
ifeq ($(UNUSED),1)
LDFLAGS += -Wl,--gc-sections
endif
$(GMQCC): $(OBJ_C) $(OBJ_D)
$(CC) -o $@ $^ $(CFLAGS)
# Enable link-time optimizations if request.
ifeq ($(LTO),1)
LDFLAGS += -flto
endif
# Sanitizer selection
ifeq ($(ASAN),1)
LDFLAGS += -fsanitize=address
endif
ifeq ($(UBSAN),1)
LDFLAGS += -fsanitize=undefined
endif
# Strip the binaries when not a debug build
ifneq (,$(findstring -g,$(CXXFLAGS)))
STRIP := true
else
STRIP := strip
endif
$(TESTSUITE): $(OBJ_T)
$(CC) -o $@ $^ $(CFLAGS)
all: $(GMQCC) $(QCVM) $(TESTSUITE)
# Build artifact directories.
$(DEPDIR):
@mkdir -p $(DEPDIR)
$(OBJDIR):
@mkdir -p $(OBJDIR)
$(OBJDIR)/%.o: %.cpp $(DEPDIR)/%.d | $(OBJDIR) $(DEPDIR)
$(CXX) -MT $@ $(DEPFLAGS) -MF $(DEPDIR)/$*.Td $(CXXFLAGS) -c -o $@ $<
@mv -f $(DEPDIR)/$*.Td $(DEPDIR)/$*.d
$(GMQCC): $(filter %.o,$(GSRCS:%.cpp=$(OBJDIR)/%.o))
$(CXX) $^ $(LDFLAGS) -o $@
$(STRIP) $@
$(QCVM): $(filter %.o,$(QSRCS:%.cpp=$(OBJDIR)/%.o))
$(CXX) $^ $(LDFLAGS) -o $@
$(STRIP) $@
$(TESTSUITE): $(filter %.o,$(TSRCS:%.cpp=$(OBJDIR)/%.o))
$(CXX) $^ $(LDFLAGS) -o $@
$(STRIP) $@
# Determine if the tests should be run.
RUNTESTS := true
ifdef TESTSUITE
RUNTESTS := ./$(TESTSUITE)
endif
test: $(QCVM) $(TESTSUITE)
@$(RUNTESTS)
check: all
@ ./$(TESTSUITE)
clean:
rm -rf $(DEPDIR) $(OBJDIR)
rm -f *.o $(GMQCC) $(QCVM) $(TESTSUITE) *.dat
.PHONY: test clean $(DEPDIR) $(OBJDIR)
# deps
$(OBJ_D) $(OBJ_C) $(OBJ_X): gmqcc.h opts.def
main.o: lexer.h
parser.o: ast.h lexer.h
lexer.o: lexer.h
ast.o: ast.h ir.h
ir.o: ir.h
# Dependencies
$(filter %.d,$(GSRCS:%.cpp=$(DEPDIR)/%.d)):
include $(wildcard $@)
$(filter %.d,$(QSRCS:%.cpp=$(DEPDIR)/%.d)):
include $(wildcard $@)
$(filter %.d,$(TSRCS:%.cpp=$(DEPDIR)/%.d)):
include $(wildcard $@)
#install rules
install: install-gmqcc install-qcvm install-doc
install-gmqcc: $(GMQCC)
install -d -m755 $(DESTDIR)$(BINDIR)
install -m755 $(GMQCC) $(DESTDIR)$(BINDIR)/gmqcc
install-qcvm: $(QCVM)
install -d -m755 $(DESTDIR)$(BINDIR)
install -m755 $(QCVM) $(DESTDIR)$(BINDIR)/qcvm
install-doc:
install -d -m755 $(DESTDIR)$(MANDIR)/man1
install -m755 doc/gmqcc.1 $(DESTDIR)$(MANDIR)/man1/
install -m755 doc/qcvm.1 $(DESTDIR)$(MANDIR)/man1/

10
README
View file

@ -1 +1,9 @@
An improved QuakeC compiler
GMQCC: An improved Quake C compiler
For licensing: see the LICENSE file.
For installation notes: see the INSTALL file.
For a list of authors: see the AUTHORS file.
For documentation:
See the manpages, or visit the documentation online at
http://graphitemaster.github.com/gmqcc/doc.html

157
TODO Normal file
View file

@ -0,0 +1,157 @@
GMQCC is quite feature compleat. But that doesn't address the fact that
it can be improved. This is a list of things that we'd like to support
in the distant future. When the time comes, we can just select a topic
from here and open a ticket for it on the issue tracker. But for the
meantime, this is sort of a cultivating flat file database.
Optimizations:
The following are optimizations that can be implemented after the
transformation into static-single assignment (SSA).
Global Value Numbering:
Eliminate redundancy by constructing a value graph of the source
then determinging which values are computed by equivalent expressions.
Simaler to Common Subexpression Elimination (CSE), however expressions
are determined via underlying equivalnce, opposed to lexically identical
expressions (CSE).
Spare Conditional Constant Propogation:
Simultaneously remove dead code and propogates constants. This is
not the same as indivial dead code elimination and constant propogation
passes. This is multipass.
The following are optimizations that can be implemented before the
transformation into a binary (code generator).
Code factoring:
The process of finding sequences of code that are identical,
or can be parameterized or reordered to be identical.
Which can be replaced with calls to a shared subroutine. To
reduce duplicated code. (Size optimization)
The following are optimizations that can be implemented anywhere, ideally
these are functional language optimizations.
Removing Recrusion:
Tail recrusive algorithms can be converted to iteration, which
does not have to have call overhead.
Language Features:
The following are language features that we'd like to see implemented in the
future.
Enumerations:
Like C
AST Macros:
Macros with sanity. Not textual subsitution.
Classes:
Like C++, but minus the stupidity:
- No type operator overloads
- Keep operator overloading for basic operators though.
- No inheritence
- No virtuals / pure virtuals
- Essentially "C structs but with operators" :)
Arrays:
They're currently implemented, but support in the engine
plus implicit bounds checks (and ability to turn the bounds
checking off)
Exceptions:
I feel like all languages suck at implementing this. This would
require support from the engine, but it would help catch bugs. We
could make it fast using a neat method of "frame pointers".
Overloaded Functions:
Ability to make individual functions with the same name, but take
different amount of arguments or type of arguments.
Default Argument Subsitution:
Ability to specify default values for arguments in functions.
void foo(string bar, string baz="default");
Supplying just one argument will expand the second argument to
become "default", otherwise if two arguments are specified then
the "default" string is overrode with what ever the user passes.
Character Type:
A char type would be nice to have. Essentially implemented as a
string, we can both "get" and "set" indices inside strings with
the help of builtin functions.
{
string foo = "test";
foo[0] = 'r';
print("it's time to ", foo);
}
Array Accessor With C-Semantics:
Also the abilit to use them as array accessors:
{
float hugearray['Z'];
hugearray['a'] = 100.0f;
}
Keep existing "pointer-like" semantics as well. In C arrays
simple work as pointers, a[1] -> *(a+1), or 1[a] -> *(1+a)
so we should allow both forms of syntax. As well as operand
reversal.
{
float h['Z'];
*(h+'a') = 100;
*('a'+h) = 'a'[h];
}
FTEQCC Inline Assembly:
This is still up for debate, mainly because a) it's syntax is
just utter crap. b) If we do an assembler, it should be nice.
we could provide a -std=fteqcc for the assembler itself :P
just like the compiler; although I think that's just insane.
Please see Assembler below.
Namespaces:
There is already a ticket open on this. They'd work just like C++
identically even.
Standalone QCVM:
The following are QCVM additions:
Proper ASM dissasembly:
Proper dissasembly of compiled .dat files. Annotated if possible
when -g (is used during compilation)
Debugging:
A step-through debuger -d (with seperate compilation as well)
Called -> qcdb Optionally alias to qcvm -d :)
We should beable to see the assembly and source it matches to
and the state of OFS_* and calls.
Testsuite:
The followiung are things we'd like to see added to the testsuite
in the distant future:
Multithreading:
Chances are when we start adding more and more tests, executing
them individually will be midly slow (even if that's a whole minute)
It would be nice to add a -j paramater to allow multiple threads to
be used and so we can execute many tests in parallel.
Interface:
Ability to select individual tests, or set paramaters manually
opposed to using the static task-template files. (A method to
override them rather).
Assembler:
Possibly support for a future assembler for QCASM. But we're not
entierly sure if it makes sense.

18
algo.h
View file

@ -1,18 +0,0 @@
#ifndef GMQCC_ALGO_HDR
#define GMQCC_ALGO_HDR
namespace algo {
template<typename ITER>
void shiftback(ITER element, ITER end) {
//typename ITER::value_type backup(move(*element)); // hold the element
typename std::remove_reference<decltype(*element)>::type backup(move(*element)); // hold the element
ITER p = element++;
for (; element != end; p = element++)
*p = move(*element);
*p = move(backup);
}
} // ::algo
#endif

2947
ast.c Normal file

File diff suppressed because it is too large Load diff

3125
ast.cpp

File diff suppressed because it is too large Load diff

825
ast.h

File diff suppressed because it is too large Load diff

259
code.c Normal file
View file

@ -0,0 +1,259 @@
/*
* Copyright (C) 2012
* Dale Weiler
* Wolfgang Bumiller
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "gmqcc.h"
prog_section_statement *code_statements;
int *code_linenums;
prog_section_def *code_defs;
prog_section_field *code_fields;
prog_section_function *code_functions;
int *code_globals;
char *code_chars;
uint16_t code_crc;
uint32_t code_entfields;
void code_push_statement(prog_section_statement *stmt, int linenum)
{
vec_push(code_statements, *stmt);
vec_push(code_linenums, linenum);
}
void code_pop_statement()
{
vec_pop(code_statements);
vec_pop(code_linenums);
}
void code_init() {
prog_section_function empty_function = {0,0,0,0,0,0,0,{0}};
prog_section_statement empty_statement = {0,{0},{0},{0}};
prog_section_def empty_def = {0, 0, 0};
int i = 0;
code_entfields = 0;
/*
* The way progs.dat is suppose to work is odd, there needs to be
* some null (empty) statements, functions, and 28 globals
*/
for(; i < 28; i++)
vec_push(code_globals, 0);
vec_push(code_chars, '\0');
vec_push(code_functions, empty_function);
code_push_statement(&empty_statement, 0);
vec_push(code_defs, empty_def);
vec_push(code_fields, empty_def);
}
uint32_t code_genstring(const char *str)
{
uint32_t off = vec_size(code_chars);
while (*str) {
vec_push(code_chars, *str);
++str;
}
vec_push(code_chars, 0);
return off;
}
uint32_t code_cachedstring(const char *str)
{
size_t s = 0;
/* We could implement knuth-morris-pratt or something
* and also take substrings, but I'm uncomfortable with
* pointing to subparts of strings for the sake of clarity...
*/
while (s < vec_size(code_chars)) {
if (!strcmp(str, code_chars + s))
return s;
while (code_chars[s]) ++s;
++s;
}
return code_genstring(str);
}
qcint code_alloc_field (size_t qcsize)
{
qcint pos = (qcint)code_entfields;
code_entfields += qcsize;
return pos;
}
bool code_write(const char *filename, const char *lnofile) {
prog_header code_header;
FILE *fp = NULL;
size_t it = 2;
code_header.statements.offset = sizeof(prog_header);
code_header.statements.length = vec_size(code_statements);
code_header.defs.offset = code_header.statements.offset + (sizeof(prog_section_statement) * vec_size(code_statements));
code_header.defs.length = vec_size(code_defs);
code_header.fields.offset = code_header.defs.offset + (sizeof(prog_section_def) * vec_size(code_defs));
code_header.fields.length = vec_size(code_fields);
code_header.functions.offset = code_header.fields.offset + (sizeof(prog_section_field) * vec_size(code_fields));
code_header.functions.length = vec_size(code_functions);
code_header.globals.offset = code_header.functions.offset + (sizeof(prog_section_function) * vec_size(code_functions));
code_header.globals.length = vec_size(code_globals);
code_header.strings.offset = code_header.globals.offset + (sizeof(int32_t) * vec_size(code_globals));
code_header.strings.length = vec_size(code_chars);
code_header.version = 6;
if (opts.forcecrc)
code_header.crc16 = opts.forced_crc;
else
code_header.crc16 = code_crc;
code_header.entfield = code_entfields;
if (OPTS_FLAG(DARKPLACES_STRING_TABLE_BUG)) {
util_debug("GEN", "Patching stringtable for -fdarkplaces-stringtablebug\n");
/* >= + P */
vec_push(code_chars, '\0'); /* > */
vec_push(code_chars, '\0'); /* = */
vec_push(code_chars, '\0'); /* P */
}
/* ensure all data is in LE format */
util_endianswap(&code_header.version, 1, sizeof(code_header.version));
util_endianswap(&code_header.crc16, 1, sizeof(code_header.crc16));
util_endianswap(&code_header.statements, 2, sizeof(code_header.statements.offset));
util_endianswap(&code_header.defs, 2, sizeof(code_header.statements.offset));
util_endianswap(&code_header.fields, 2, sizeof(code_header.statements.offset));
util_endianswap(&code_header.functions, 2, sizeof(code_header.statements.offset));
util_endianswap(&code_header.strings, 2, sizeof(code_header.statements.offset));
util_endianswap(&code_header.globals, 2, sizeof(code_header.statements.offset));
util_endianswap(&code_header.entfield, 1, sizeof(code_header.entfield));
util_endianswap(code_statements, vec_size(code_statements), sizeof(prog_section_statement));
util_endianswap(code_defs, vec_size(code_defs), sizeof(prog_section_def));
util_endianswap(code_fields, vec_size(code_fields), sizeof(prog_section_field));
util_endianswap(code_functions, vec_size(code_functions), sizeof(prog_section_function));
util_endianswap(code_globals, vec_size(code_globals), sizeof(int32_t));
if (lnofile) {
uint32_t lnotype = *(unsigned int*)"LNOF";
uint32_t version = 1;
fp = file_open(lnofile, "wb");
if (!fp)
return false;
util_endianswap(&version, 1, sizeof(version));
util_endianswap(code_linenums, vec_size(code_linenums), sizeof(code_linenums[0]));
if (file_write(&lnotype, sizeof(lnotype), 1, fp) != 1 ||
file_write(&version, sizeof(version), 1, fp) != 1 ||
file_write(&code_header.defs.length, sizeof(code_header.defs.length), 1, fp) != 1 ||
file_write(&code_header.globals.length, sizeof(code_header.globals.length), 1, fp) != 1 ||
file_write(&code_header.fields.length, sizeof(code_header.fields.length), 1, fp) != 1 ||
file_write(&code_header.statements.length, sizeof(code_header.statements.length), 1, fp) != 1 ||
file_write(code_linenums, sizeof(code_linenums[0]), vec_size(code_linenums), fp) != vec_size(code_linenums))
{
con_err("failed to write lno file\n");
}
file_close(fp);
fp = NULL;
}
fp = file_open(filename, "wb");
if (!fp)
return false;
if (1 != file_write(&code_header, sizeof(prog_header) , 1 , fp) ||
vec_size(code_statements) != file_write(code_statements, sizeof(prog_section_statement), vec_size(code_statements), fp) ||
vec_size(code_defs) != file_write(code_defs, sizeof(prog_section_def) , vec_size(code_defs) , fp) ||
vec_size(code_fields) != file_write(code_fields, sizeof(prog_section_field) , vec_size(code_fields) , fp) ||
vec_size(code_functions) != file_write(code_functions, sizeof(prog_section_function) , vec_size(code_functions) , fp) ||
vec_size(code_globals) != file_write(code_globals, sizeof(int32_t) , vec_size(code_globals) , fp) ||
vec_size(code_chars) != file_write(code_chars, 1 , vec_size(code_chars) , fp))
{
file_close(fp);
return false;
}
util_debug("GEN","HEADER:\n");
util_debug("GEN"," version: = %d\n", code_header.version );
util_debug("GEN"," crc16: = %d\n", code_header.crc16 );
util_debug("GEN"," entfield: = %d\n", code_header.entfield);
util_debug("GEN"," statements = {.offset = % 8d, .length = % 8d}\n", code_header.statements.offset, code_header.statements.length);
util_debug("GEN"," defs = {.offset = % 8d, .length = % 8d}\n", code_header.defs .offset, code_header.defs .length);
util_debug("GEN"," fields = {.offset = % 8d, .length = % 8d}\n", code_header.fields .offset, code_header.fields .length);
util_debug("GEN"," functions = {.offset = % 8d, .length = % 8d}\n", code_header.functions .offset, code_header.functions .length);
util_debug("GEN"," globals = {.offset = % 8d, .length = % 8d}\n", code_header.globals .offset, code_header.globals .length);
util_debug("GEN"," strings = {.offset = % 8d, .length = % 8d}\n", code_header.strings .offset, code_header.strings .length);
/* FUNCTIONS */
util_debug("GEN", "FUNCTIONS:\n");
for (; it < vec_size(code_functions); it++) {
size_t j = code_functions[it].entry;
util_debug("GEN", " {.entry =% 5d, .firstlocal =% 5d, .locals =% 5d, .profile =% 5d, .name =% 5d, .file =% 5d, .nargs =% 5d, .argsize ={%d,%d,%d,%d,%d,%d,%d,%d} }\n",
code_functions[it].entry,
code_functions[it].firstlocal,
code_functions[it].locals,
code_functions[it].profile,
code_functions[it].name,
code_functions[it].file,
code_functions[it].nargs,
code_functions[it].argsize[0],
code_functions[it].argsize[1],
code_functions[it].argsize[2],
code_functions[it].argsize[3],
code_functions[it].argsize[4],
code_functions[it].argsize[5],
code_functions[it].argsize[6],
code_functions[it].argsize[7]
);
util_debug("GEN", " NAME: %s\n", &code_chars[code_functions[it].name]);
/* Internal functions have no code */
if (code_functions[it].entry >= 0) {
util_debug("GEN", " CODE:\n");
for (;;) {
if (code_statements[j].opcode != INSTR_DONE)
util_debug("GEN", " %-12s {% 5i,% 5i,% 5i}\n",
asm_instr[code_statements[j].opcode].m,
code_statements[j].o1.s1,
code_statements[j].o2.s1,
code_statements[j].o3.s1
);
else {
util_debug("GEN", " DONE {0x00000,0x00000,0x00000}\n");
break;
}
j++;
}
}
}
vec_free(code_statements);
vec_free(code_linenums);
vec_free(code_defs);
vec_free(code_fields);
vec_free(code_functions);
vec_free(code_globals);
vec_free(code_chars);
file_close(fp);
return true;
}

348
code.cpp
View file

@ -1,348 +0,0 @@
#include <string.h>
#include "gmqcc.h"
/*
* We could use the old method of casting to uintptr_t then to void*
* or qcint_t; however, it's incredibly unsafe for two reasons.
* 1) The compilers aliasing optimization can legally make it unstable
* (it's undefined behaviour).
*
* 2) The cast itself depends on fresh storage (newly allocated in which
* ever function is using the cast macros), the contents of which are
* transferred in a way that the obligation to release storage is not
* propagated.
*/
typedef union {
void *enter;
qcint_t leave;
} code_hash_entry_t;
/* Some sanity macros */
#define CODE_HASH_ENTER(ENTRY) ((ENTRY).enter)
#define CODE_HASH_LEAVE(ENTRY) ((ENTRY).leave)
void code_push_statement(code_t *code, prog_section_statement_t *stmt_in, lex_ctx_t ctx)
{
prog_section_statement_t stmt = *stmt_in;
if (OPTS_FLAG(TYPELESS_STORES)) {
switch (stmt.opcode) {
case INSTR_LOAD_S:
case INSTR_LOAD_ENT:
case INSTR_LOAD_FLD:
case INSTR_LOAD_FNC:
stmt.opcode = INSTR_LOAD_F;
break;
case INSTR_STORE_S:
case INSTR_STORE_ENT:
case INSTR_STORE_FLD:
case INSTR_STORE_FNC:
stmt.opcode = INSTR_STORE_F;
break;
case INSTR_STOREP_S:
case INSTR_STOREP_ENT:
case INSTR_STOREP_FLD:
case INSTR_STOREP_FNC:
stmt.opcode = INSTR_STOREP_F;
break;
}
}
if (OPTS_FLAG(SORT_OPERANDS)) {
uint16_t pair;
switch (stmt.opcode) {
case INSTR_MUL_F:
case INSTR_MUL_V:
case INSTR_ADD_F:
case INSTR_EQ_F:
case INSTR_EQ_S:
case INSTR_EQ_E:
case INSTR_EQ_FNC:
case INSTR_NE_F:
case INSTR_NE_V:
case INSTR_NE_S:
case INSTR_NE_E:
case INSTR_NE_FNC:
case INSTR_AND:
case INSTR_OR:
case INSTR_BITAND:
case INSTR_BITOR:
if (stmt.o1.u1 < stmt.o2.u1) {
uint16_t a = stmt.o2.u1;
stmt.o1.u1 = stmt.o2.u1;
stmt.o2.u1 = a;
}
break;
case INSTR_MUL_VF: pair = INSTR_MUL_FV; goto case_pair_gen;
case INSTR_MUL_FV: pair = INSTR_MUL_VF; goto case_pair_gen;
case INSTR_LT: pair = INSTR_GT; goto case_pair_gen;
case INSTR_GT: pair = INSTR_LT; goto case_pair_gen;
case INSTR_LE: pair = INSTR_GE; goto case_pair_gen;
case INSTR_GE: pair = INSTR_LE;
case_pair_gen:
if (stmt.o1.u1 < stmt.o2.u1) {
uint16_t x = stmt.o1.u1;
stmt.o1.u1 = stmt.o2.u1;
stmt.o2.u1 = x;
stmt.opcode = pair;
}
break;
}
}
code->statements.push_back(stmt);
code->linenums.push_back(ctx.line);
code->columnnums.push_back(ctx.column);
}
void code_pop_statement(code_t *code)
{
code->statements.pop_back();
code->linenums.pop_back();
code->columnnums.pop_back();
}
void *code_t::operator new(std::size_t bytes) {
return mem_a(bytes);
}
void code_t::operator delete(void *ptr) {
mem_d(ptr);
}
code_t::code_t()
{
static lex_ctx_t empty_ctx = {0, 0, 0};
static prog_section_function_t empty_function = {0,0,0,0,0,0,0,{0,0,0,0,0,0,0,0}};
static prog_section_statement_t empty_statement = {0,{0},{0},{0}};
static prog_section_def_t empty_def = {0, 0, 0};
string_cache = util_htnew(OPTS_OPTIMIZATION(OPTIM_OVERLAP_STRINGS) ? 0x100 : 1024);
// The way progs.dat is suppose to work is odd, there needs to be
// some null (empty) statements, functions, and 28 globals
globals.insert(globals.begin(), 28, 0);
chars.push_back('\0');
functions.push_back(empty_function);
code_push_statement(this, &empty_statement, empty_ctx);
defs.push_back(empty_def);
fields.push_back(empty_def);
}
code_t::~code_t()
{
util_htdel(string_cache);
}
void *code_util_str_htgeth(hash_table_t *ht, const char *key, size_t bin);
uint32_t code_genstring(code_t *code, const char *str) {
size_t hash;
code_hash_entry_t existing;
if (!str)
return 0;
if (!*str) {
if (!code->string_cached_empty) {
code->string_cached_empty = code->chars.size();
code->chars.push_back(0);
}
return code->string_cached_empty;
}
if (OPTS_OPTIMIZATION(OPTIM_OVERLAP_STRINGS)) {
hash = ((unsigned char*)str)[strlen(str)-1];
CODE_HASH_ENTER(existing) = code_util_str_htgeth(code->string_cache, str, hash);
} else {
hash = util_hthash(code->string_cache, str);
CODE_HASH_ENTER(existing) = util_htgeth(code->string_cache, str, hash);
}
if (CODE_HASH_ENTER(existing))
return CODE_HASH_LEAVE(existing);
CODE_HASH_LEAVE(existing) = code->chars.size();
code->chars.insert(code->chars.end(), str, str + strlen(str) + 1);
util_htseth(code->string_cache, str, hash, CODE_HASH_ENTER(existing));
return CODE_HASH_LEAVE(existing);
}
qcint_t code_alloc_field (code_t *code, size_t qcsize)
{
qcint_t pos = (qcint_t)code->entfields;
code->entfields += qcsize;
return pos;
}
static size_t code_size_generic(code_t *code, prog_header_t *code_header, bool lno) {
size_t size = 0;
if (lno) {
size += 4; /* LNOF */
size += sizeof(uint32_t); /* version */
size += sizeof(code_header->defs.length);
size += sizeof(code_header->globals.length);
size += sizeof(code_header->fields.length);
size += sizeof(code_header->statements.length);
size += sizeof(code->linenums[0]) * code->linenums.size();
size += sizeof(code->columnnums[0]) * code->columnnums.size();
} else {
size += sizeof(prog_header_t);
size += sizeof(prog_section_statement_t) * code->statements.size();
size += sizeof(prog_section_def_t) * code->defs.size();
size += sizeof(prog_section_field_t) * code->fields.size();
size += sizeof(prog_section_function_t) * code->functions.size();
size += sizeof(int32_t) * code->globals.size();
size += 1 * code->chars.size();
}
return size;
}
#define code_size_binary(C, H) code_size_generic((C), (H), false)
#define code_size_debug(C, H) code_size_generic((C), (H), true)
static void code_create_header(code_t *code, prog_header_t *code_header, const char *filename, const char *lnofile) {
size_t i;
code_header->statements.offset = sizeof(prog_header_t);
code_header->statements.length = code->statements.size();
code_header->defs.offset = code_header->statements.offset + (sizeof(prog_section_statement_t) * code->statements.size());
code_header->defs.length = code->defs.size();
code_header->fields.offset = code_header->defs.offset + (sizeof(prog_section_def_t) * code->defs.size());
code_header->fields.length = code->fields.size();
code_header->functions.offset = code_header->fields.offset + (sizeof(prog_section_field_t) * code->fields.size());
code_header->functions.length = code->functions.size();
code_header->globals.offset = code_header->functions.offset + (sizeof(prog_section_function_t) * code->functions.size());
code_header->globals.length = code->globals.size();
code_header->strings.offset = code_header->globals.offset + (sizeof(int32_t) * code->globals.size());
code_header->strings.length = code->chars.size();
code_header->version = 6;
code_header->skip = 0;
if (OPTS_OPTION_BOOL(OPTION_FORCECRC))
code_header->crc16 = OPTS_OPTION_U16(OPTION_FORCED_CRC);
else
code_header->crc16 = code->crc;
code_header->entfield = code->entfields;
if (OPTS_FLAG(DARKPLACES_STRING_TABLE_BUG)) {
/* >= + P */
code->chars.push_back('\0'); /* > */
code->chars.push_back('\0'); /* = */
code->chars.push_back('\0'); /* P */
}
/* ensure all data is in LE format */
util_swap_header(*code_header);
util_swap_statements(code->statements);
util_swap_defs_fields(code->defs);
util_swap_defs_fields(code->fields);
util_swap_functions(code->functions);
util_swap_globals(code->globals);
if (!OPTS_OPTION_BOOL(OPTION_QUIET)) {
if (lnofile)
con_out("writing '%s' and '%s'...\n", filename, lnofile);
else
con_out("writing '%s'\n", filename);
}
if (!OPTS_OPTION_BOOL(OPTION_QUIET) &&
!OPTS_OPTION_BOOL(OPTION_PP_ONLY))
{
char buffer[1024];
con_out("\nOptimizations:\n");
for (i = 0; i < COUNT_OPTIMIZATIONS; ++i) {
if (opts_optimizationcount[i]) {
util_optimizationtostr(opts_opt_list[i].name, buffer, sizeof(buffer));
con_out(
" %s: %u\n",
buffer,
(unsigned int)opts_optimizationcount[i]
);
}
}
}
}
static void code_stats(const char *filename, const char *lnofile, code_t *code, prog_header_t *code_header) {
if (OPTS_OPTION_BOOL(OPTION_QUIET) ||
OPTS_OPTION_BOOL(OPTION_PP_ONLY))
return;
con_out("\nFile statistics:\n");
con_out(" dat:\n");
con_out(" name: %s\n", filename);
con_out(" size: %u (bytes)\n", code_size_binary(code, code_header));
con_out(" crc: 0x%04X\n", code->crc);
if (lnofile) {
con_out(" lno:\n");
con_out(" name: %s\n", lnofile);
con_out(" size: %u (bytes)\n", code_size_debug(code, code_header));
}
con_out("\n");
}
bool code_write(code_t *code, const char *filename, const char *lnofile) {
prog_header_t code_header;
FILE *fp = nullptr;
code_create_header(code, &code_header, filename, lnofile);
if (lnofile) {
uint32_t version = 1;
fp = fopen(lnofile, "wb");
if (!fp)
return false;
util_endianswap(&version, 1, sizeof(version));
util_endianswap(&code->linenums[0], code->linenums.size(), sizeof(code->linenums[0]));
util_endianswap(&code->columnnums[0], code->columnnums.size(), sizeof(code->columnnums[0]));
if (fwrite("LNOF", 4, 1, fp) != 1 ||
fwrite(&version, sizeof(version), 1, fp) != 1 ||
fwrite(&code_header.defs.length, sizeof(code_header.defs.length), 1, fp) != 1 ||
fwrite(&code_header.globals.length, sizeof(code_header.globals.length), 1, fp) != 1 ||
fwrite(&code_header.fields.length, sizeof(code_header.fields.length), 1, fp) != 1 ||
fwrite(&code_header.statements.length, sizeof(code_header.statements.length), 1, fp) != 1 ||
fwrite(&code->linenums[0], sizeof(code->linenums[0]), code->linenums.size(), fp) != code->linenums.size() ||
fwrite(&code->columnnums[0], sizeof(code->columnnums[0]), code->columnnums.size(), fp) != code->columnnums.size())
{
con_err("failed to write lno file\n");
}
fclose(fp);
fp = nullptr;
}
fp = fopen(filename, "wb");
if (!fp)
return false;
if (1 != fwrite(&code_header, sizeof(prog_header_t) , 1 , fp) ||
code->statements.size() != fwrite(&code->statements[0], sizeof(prog_section_statement_t), code->statements.size(), fp) ||
code->defs.size() != fwrite(&code->defs[0], sizeof(prog_section_def_t) , code->defs.size() , fp) ||
code->fields.size() != fwrite(&code->fields[0], sizeof(prog_section_field_t) , code->fields.size() , fp) ||
code->functions.size() != fwrite(&code->functions[0], sizeof(prog_section_function_t) , code->functions.size() , fp) ||
code->globals.size() != fwrite(&code->globals[0], sizeof(int32_t) , code->globals.size() , fp) ||
code->chars.size() != fwrite(&code->chars[0], 1 , code->chars.size() , fp))
{
fclose(fp);
return false;
}
fclose(fp);
code_stats(filename, lnofile, code, &code_header);
return true;
}

433
conout.c Normal file
View file

@ -0,0 +1,433 @@
/*
* Copyright (C) 2012
* Dale Weiler
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "gmqcc.h"
#include <stdio.h>
/*
* isatty/STDERR_FILENO/STDOUT_FILNO
* + some other things likewise.
*/
#ifndef _WIN32
# include <unistd.h>
#else
# include <io.h>
/*
* Windows and it's posix underscore bullshit. We simply fix this
* with yay, another macro :P
*/
# define isatty _isatty
#endif
#define GMQCC_IS_STDOUT(X) ((FILE*)((void*)X) == stdout)
#define GMQCC_IS_STDERR(X) ((FILE*)((void*)X) == stderr)
#define GMQCC_IS_DEFINE(X) (GMQCC_IS_STDERR(X) || GMQCC_IS_STDOUT(X))
typedef struct {
FILE *handle_err;
FILE *handle_out;
int color_err;
int color_out;
} con_t;
/*
* Doing colored output on windows is fucking stupid. The linux way is
* the real way. So we emulate it on windows :)
*/
#ifdef _MSC_VER
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
/*
* Windows doesn't have constants for FILENO, sadly but the docs tell
* use the constant values.
*/
#undef STDERR_FILENO
#undef STDOUT_FILENO
#define STDERR_FILENO 2
#define STDOUT_FILENO 1
enum {
RESET = 0,
BOLD = 1,
BLACK = 30,
RED,
GREEN,
YELLOW,
BLUE,
MAGENTA,
CYAN,
GRAY,
WHITE = GRAY
};
enum {
WBLACK,
WBLUE,
WGREEN = 2,
WRED = 4,
WINTENSE = 8,
WCYAN = WBLUE | WGREEN,
WMAGENTA = WBLUE | WRED,
WYELLOW = WGREEN | WRED,
WWHITE = WBLUE | WGREEN | WRED
};
static const int ansi2win[] = {
WBLACK,
WRED,
WGREEN,
WYELLOW,
WBLUE,
WMAGENTA,
WCYAN,
WWHITE
};
static int win_fputs(const char *str, FILE *h) {
/* state for translate */
int acolor;
int wcolor;
int icolor;
int state;
/* attributes */
int intense = -1;
int colors[] = {-1, -1 };
int colorpos = 1;
int length = 0;
CONSOLE_SCREEN_BUFFER_INFO cinfo;
GetConsoleScreenBufferInfo (
(GMQCC_IS_STDOUT(h)) ?
GetStdHandle(STD_OUTPUT_HANDLE) :
GetStdHandle(STD_ERROR_HANDLE), &cinfo
);
icolor = cinfo.wAttributes;
while (*str) {
if (*str == '\x1B')
state = '\x1B';
else if (state == '\x1B' && *str == '[')
state = '[';
else if (state == '[') {
if (*str != 'm') {
colors[colorpos] = *str;
colorpos--;
} else {
int find;
int mult;
for (find = colorpos + 1, acolor = 0, mult = 1; find < 2; find++) {
acolor += (colors[find] - 48) * mult;
mult *= 10;
}
/* convert to windows color */
if (acolor == BOLD)
intense = WINTENSE;
else if (acolor == RESET) {
intense = WBLACK;
wcolor = icolor;
}
else if (BLACK <= acolor && acolor <= WHITE)
wcolor = ansi2win[acolor - 30];
else if (acolor == 90) {
/* special gray really white man */
wcolor = WWHITE;
intense = WBLACK;
}
SetConsoleTextAttribute (
(GMQCC_IS_STDOUT(h)) ?
GetStdHandle(STD_OUTPUT_HANDLE) :
GetStdHandle(STD_ERROR_HANDLE),
wcolor | intense | (icolor & 0xF0)
);
colorpos = 1;
state = -1;
}
} else {
file_putc(*str, h);
length ++;
}
str++;
}
/* restore */
SetConsoleTextAttribute(
(GMQCC_IS_STDOUT(h)) ?
GetStdHandle(STD_OUTPUT_HANDLE) :
GetStdHandle(STD_ERROR_HANDLE),
icolor
);
return length;
}
#endif
/*
* We use standard files as default. These can be changed at any time
* with con_change(F, F)
*/
static con_t console;
/*
* Enables color on output if supported.
* NOTE: The support for checking colors is NULL. On windows this will
* always work, on *nix it depends if the term has colors.
*
* NOTE: This prevents colored output to piped stdout/err via isatty
* checks.
*/
static void con_enablecolor() {
if (console.handle_err == stderr || console.handle_err == stdout)
console.color_err = !!(isatty(STDERR_FILENO));
if (console.handle_out == stderr || console.handle_out == stdout)
console.color_out = !!(isatty(STDOUT_FILENO));
}
/*
* Does a write to the handle with the format string and list of
* arguments. This colorizes for windows as well via translate
* step.
*/
static int con_write(FILE *handle, const char *fmt, va_list va) {
int ln;
#ifndef _MSC_VER
ln = vfprintf(handle, fmt, va);
#else
{
char data[4096];
memset(data, 0, sizeof(data));
vsnprintf(data, sizeof(data), fmt, va);
ln = (GMQCC_IS_DEFINE(handle)) ? win_fputs(data, handle) : file_puts(data, handle);
}
#endif
return ln;
}
/**********************************************************************
* EXPOSED INTERFACE BEGINS
*********************************************************************/
void con_close() {
if (!GMQCC_IS_DEFINE(console.handle_err))
file_close(console.handle_err);
if (!GMQCC_IS_DEFINE(console.handle_out))
file_close(console.handle_out);
}
void con_color(int state) {
if (state)
con_enablecolor();
else {
console.color_err = 0;
console.color_out = 0;
}
}
void con_init() {
console.handle_err = stderr;
console.handle_out = stdout;
con_enablecolor();
}
void con_reset() {
con_close();
con_init ();
}
/*
* This is clever, say you want to change the console to use two
* files for out/err. You pass in two strings, it will properly
* close the existing handles (if they're not std* handles) and
* open them. Now say you want TO use stdout and stderr, this
* allows you to do that so long as you cast them to (char*).
* Say you need stdout for out, but want a file for error, you can
* do this too, just cast the stdout for (char*) and stick to a
* string for the error file.
*/
int con_change(const char *out, const char *err) {
con_close();
if (!out) out = (const char *)((!console.handle_out) ? stdout : console.handle_out);
if (!err) err = (const char *)((!console.handle_err) ? stderr : console.handle_err);
if (GMQCC_IS_DEFINE(out)) {
console.handle_out = GMQCC_IS_STDOUT(out) ? stdout : stderr;
con_enablecolor();
} else if (!(console.handle_out = file_open(out, "w"))) return 0;
if (GMQCC_IS_DEFINE(err)) {
console.handle_err = GMQCC_IS_STDOUT(err) ? stdout : stderr;
con_enablecolor();
} else if (!(console.handle_err = file_open(err, "w"))) return 0;
/* no buffering */
setvbuf(console.handle_out, NULL, _IONBF, 0);
setvbuf(console.handle_err, NULL, _IONBF, 0);
return 1;
}
/*
* Defaultizer because stdio.h shouldn't be used anywhere except here
* and inside file.c To prevent mis-match of wrapper-interfaces.
*/
FILE *con_default_out() {
return (console.handle_out = stdout);
}
FILE *con_default_err() {
return (console.handle_err = stderr);
}
int con_verr(const char *fmt, va_list va) {
return con_write(console.handle_err, fmt, va);
}
int con_vout(const char *fmt, va_list va) {
return con_write(console.handle_out, fmt, va);
}
/*
* Standard stdout/stderr printf functions used generally where they need
* to be used.
*/
int con_err(const char *fmt, ...) {
va_list va;
int ln = 0;
va_start(va, fmt);
con_verr(fmt, va);
va_end (va);
return ln;
}
int con_out(const char *fmt, ...) {
va_list va;
int ln = 0;
va_start(va, fmt);
con_vout(fmt, va);
va_end (va);
return ln;
}
/*
* Utility console message writes for lexer contexts. These will allow
* for reporting of file:line based on lexer context, These are used
* heavily in the parser/ir/ast.
*/
void con_vprintmsg_c(int level, const char *name, size_t line, const char *msgtype, const char *msg, va_list ap, const char *condname) {
/* color selection table */
static int sel[] = {
CON_WHITE,
CON_CYAN,
CON_RED
};
int err = !!(level == LVL_ERROR);
int color = (err) ? console.color_err : console.color_out;
int (*print) (const char *, ...) = (err) ? &con_err : &con_out;
int (*vprint)(const char *, va_list) = (err) ? &con_verr : &con_vout;
if (color)
print("\033[0;%dm%s:%d: \033[0;%dm%s: \033[0m", CON_CYAN, name, (int)line, sel[level], msgtype);
else
print("%s:%d: %s: ", name, (int)line, msgtype);
vprint(msg, ap);
if (condname)
print(" [%s]\n", condname);
else
print("\n");
}
void con_vprintmsg(int level, const char *name, size_t line, const char *msgtype, const char *msg, va_list ap) {
con_vprintmsg_c(level, name, line, msgtype, msg, ap, NULL);
}
void con_printmsg(int level, const char *name, size_t line, const char *msgtype, const char *msg, ...) {
va_list va;
va_start(va, msg);
con_vprintmsg(level, name, line, msgtype, msg, va);
va_end (va);
}
void con_cvprintmsg(void *ctx, int lvl, const char *msgtype, const char *msg, va_list ap) {
con_vprintmsg(lvl, ((lex_ctx*)ctx)->file, ((lex_ctx*)ctx)->line, msgtype, msg, ap);
}
void con_cprintmsg (void *ctx, int lvl, const char *msgtype, const char *msg, ...) {
va_list va;
va_start(va, msg);
con_cvprintmsg(ctx, lvl, msgtype, msg, va);
va_end (va);
}
/* General error interface */
size_t compile_errors = 0;
size_t compile_warnings = 0;
void vcompile_error(lex_ctx ctx, const char *msg, va_list ap)
{
++compile_errors;
con_cvprintmsg((void*)&ctx, LVL_ERROR, "error", msg, ap);
}
void compile_error(lex_ctx ctx, const char *msg, ...)
{
va_list ap;
va_start(ap, msg);
vcompile_error(ctx, msg, ap);
va_end(ap);
}
bool GMQCC_WARN vcompile_warning(lex_ctx ctx, int warntype, const char *fmt, va_list ap)
{
int lvl = LVL_WARNING;
char warn_name[1024];
if (!OPTS_WARN(warntype))
return false;
warn_name[0] = '-';
warn_name[1] = 'W';
(void)util_strtononcmd(opts_warn_list[warntype].name, warn_name+2, sizeof(warn_name)-2);
if (OPTS_WERROR(warntype)) {
++compile_errors;
lvl = LVL_ERROR;
}
else
++compile_warnings;
con_vprintmsg_c(lvl, ctx.file, ctx.line, ((lvl == LVL_ERROR) ? "error" : "warning"), fmt, ap, warn_name);
return OPTS_WERROR(warntype);
}
bool GMQCC_WARN compile_warning(lex_ctx ctx, int warntype, const char *fmt, ...)
{
bool r;
va_list ap;
va_start(ap, fmt);
r = vcompile_warning(ctx, warntype, fmt, ap);
va_end(ap);
return r;
}

View file

@ -1,226 +0,0 @@
#include <stdio.h>
#include "gmqcc.h"
#define GMQCC_IS_STDOUT(X) ((X) == stdout)
#define GMQCC_IS_STDERR(X) ((X) == stderr)
#define GMQCC_IS_DEFINE(X) (GMQCC_IS_STDERR(X) || GMQCC_IS_STDOUT(X))
struct con_t {
FILE *handle_err;
FILE *handle_out;
int color_err;
int color_out;
};
static con_t console;
/*
* Enables color on output if supported.
* NOTE: The support for checking colors is nullptr. On windows this will
* always work, on *nix it depends if the term has colors.
*
* NOTE: This prevents colored output to piped stdout/err via isatty
* checks.
*/
static void con_enablecolor(void) {
console.color_err = util_isatty(console.handle_err);
console.color_out = util_isatty(console.handle_out);
}
/*
* Does a write to the handle with the format string and list of
* arguments. This colorizes for windows as well via translate
* step.
*/
static int con_write(FILE *handle, const char *fmt, va_list va) {
return vfprintf(handle, fmt, va);
}
/**********************************************************************
* EXPOSED INTERFACE BEGINS
*********************************************************************/
void con_close() {
if (!GMQCC_IS_DEFINE(console.handle_err))
fclose(console.handle_err);
if (!GMQCC_IS_DEFINE(console.handle_out))
fclose(console.handle_out);
}
void con_color(int state) {
if (state)
con_enablecolor();
else {
console.color_err = 0;
console.color_out = 0;
}
}
void con_init() {
console.handle_err = stderr;
console.handle_out = stdout;
con_enablecolor();
}
void con_reset() {
con_close();
con_init();
}
/*
* Defaultizer because stdio.h shouldn't be used anywhere except here
* and inside file.c To prevent mis-match of wrapper-interfaces.
*/
FILE *con_default_out() {
return console.handle_out = stdout;
}
FILE *con_default_err() {
return console.handle_err = stderr;
}
int con_verr(const char *fmt, va_list va) {
return con_write(console.handle_err, fmt, va);
}
int con_vout(const char *fmt, va_list va) {
return con_write(console.handle_out, fmt, va);
}
/*
* Standard stdout/stderr printf functions used generally where they need
* to be used.
*/
int con_err(const char *fmt, ...) {
va_list va;
int ln = 0;
va_start(va, fmt);
con_verr(fmt, va);
va_end(va);
return ln;
}
int con_out(const char *fmt, ...) {
va_list va;
int ln = 0;
va_start(va, fmt);
con_vout(fmt, va);
va_end (va);
return ln;
}
/*
* Utility console message writes for lexer contexts. These will allow
* for reporting of file:line based on lexer context, These are used
* heavily in the parser/ir/ast.
*/
static void con_vprintmsg_c(int level, const char *name, size_t line, size_t column, const char *msgtype, const char *msg, va_list ap, const char *condname) {
/* color selection table */
static int sel[] = {
CON_WHITE,
CON_CYAN,
CON_RED
};
int err = !!(level == LVL_ERROR);
int color = (err) ? console.color_err : console.color_out;
int (*print) (const char *, ...) = (err) ? &con_err : &con_out;
int (*vprint)(const char *, va_list) = (err) ? &con_verr : &con_vout;
if (color)
print("\033[0;%dm%s:%d:%d: \033[0;%dm%s: \033[0m", CON_CYAN, name, (int)line, (int)column, sel[level], msgtype);
else
print("%s:%d:%d: %s: ", name, (int)line, (int)column, msgtype);
vprint(msg, ap);
if (condname)
print(" [%s]\n", condname);
else
print("\n");
}
void con_vprintmsg(int level, const char *name, size_t line, size_t column, const char *msgtype, const char *msg, va_list ap) {
con_vprintmsg_c(level, name, line, column, msgtype, msg, ap, nullptr);
}
void con_printmsg(int level, const char *name, size_t line, size_t column, const char *msgtype, const char *msg, ...) {
va_list va;
va_start(va, msg);
con_vprintmsg(level, name, line, column, msgtype, msg, va);
va_end (va);
}
void con_cvprintmsg(lex_ctx_t ctx, int lvl, const char *msgtype, const char *msg, va_list ap) {
con_vprintmsg(lvl, ctx.file, ctx.line, ctx.column, msgtype, msg, ap);
}
void con_cprintmsg(lex_ctx_t ctx, int lvl, const char *msgtype, const char *msg, ...) {
va_list va;
va_start(va, msg);
con_cvprintmsg(ctx, lvl, msgtype, msg, va);
va_end (va);
}
/* General error interface: TODO seperate as part of the compiler front-end */
size_t compile_errors = 0;
size_t compile_warnings = 0;
size_t compile_Werrors = 0;
static lex_ctx_t first_werror;
void compile_show_werrors()
{
con_cprintmsg(first_werror, LVL_ERROR, "first warning", "was here");
}
void vcompile_error(lex_ctx_t ctx, const char *msg, va_list ap)
{
++compile_errors;
con_cvprintmsg(ctx, LVL_ERROR, "error", msg, ap);
}
void compile_error_(lex_ctx_t ctx, const char *msg, ...)
{
va_list ap;
va_start(ap, msg);
vcompile_error(ctx, msg, ap);
va_end(ap);
}
bool GMQCC_WARN vcompile_warning(lex_ctx_t ctx, int warntype, const char *fmt, va_list ap)
{
const char *msgtype = "warning";
int lvl = LVL_WARNING;
char warn_name[1024];
if (!OPTS_WARN(warntype))
return false;
warn_name[0] = '-';
warn_name[1] = 'W';
(void)util_strtononcmd(opts_warn_list[warntype].name, warn_name+2, sizeof(warn_name)-2);
++compile_warnings;
if (OPTS_WERROR(warntype)) {
if (!compile_Werrors)
first_werror = ctx;
++compile_Werrors;
msgtype = "Werror";
if (OPTS_FLAG(BAIL_ON_WERROR)) {
msgtype = "error";
++compile_errors;
}
lvl = LVL_ERROR;
}
con_vprintmsg_c(lvl, ctx.file, ctx.line, ctx.column, msgtype, fmt, ap, warn_name);
return OPTS_WERROR(warntype) && OPTS_FLAG(BAIL_ON_WERROR);
}
bool GMQCC_WARN compile_warning_(lex_ctx_t ctx, int warntype, const char *fmt, ...)
{
bool r;
va_list ap;
va_start(ap, fmt);
r = vcompile_warning(ctx, warntype, fmt, ap);
va_end(ap);
return r;
}

File diff suppressed because it is too large Load diff

View file

@ -1,107 +1,92 @@
.\" qcvm mdoc manpage
.Dd January 31, 2013
.Dt QCVM 1 PRM
.Os
.Sh NAME
.Nm qcvm
.Nd A standalone QuakeC VM binary executor
.Sh SYNOPSIS
.Nm qcvm
.Op Cm options
.Op Cm parameters
.Ar program-file
.Sh DESCRIPTION
.Nm qcvm
is an executor for QuakeC VM binary files created using a QC
.\" Process with groff -man -Tascii file.3
.TH QCVM 1 2012-18-12 "" "gmqcc Manual"
.SH NAME
qcvm \- A standalone QuakeC VM binary executor.
.SH SYNOPSIS
.B qcvm
[\fIOPTIONS\fR] [\fIPARAMS\fR] [\fIfile\fR]
.SH DESCRIPTION
qcvm is an executor for QuakeC VM binary files created using a QC
compiler such as gmqcc(1) or fteqcc. It provides a small set of
builtin functions, and by default executes the
.Fn main
function if there is one. Some options useful for debugging are
available as well.
.Sh OPTIONS
builtin functions, and by default executes the \fImain\fR function if
there is one. Some options useful for debugging are available as well.
.SH OPTIONS
There are 2 types of options. Options for the executor, and parameter
options used to add parameters which are passed to the main function
on execution.
.Bl -tag -width Ds
.It Fl h , Fl -help
.TP
.B "-h, --help"
Show a usage message and exit.
.It Fl trace
.TP
.B "-trace"
Trace the execution. Each instruction will be printed to stdout before
executing it.
.It Fl profile
.TP
.B "-profile"
Perform some profiling. This is currently not really implemented, the
option is available nonetheless.
.It Fl info
.TP
.B "-info"
Print information from the program's header instead of executing.
.It Fl disasm
.TP
.B "-disasm"
Disassemble the program by function instead of executing.
.It Fl disasm-func Ar function
Search for and disassemble the given function.
.It Fl printdefs
.TP
.B "-printdefs"
List all entries from the program's defs-section. Effectively
listing all the global variables of the program.
This option disables execution.
.It Fl printfields
.TP
.B "-printfields"
List all entries from the program's fields-section. Listing all
entity-fields declared in the program.
This option disables execution.
.It Fl printfuns
.TP
.B "-printfuns"
List functions and some information about their parameters.
This option disables execution. With a verbosity level of 1, builtin
numbers are printed. With a verbosity of 2, the function's sizes are
printed as well. This takes a little longer since the size is found by
searching for a
.Ql DONE
instruction in the code.
.It Fl v
Increase verbosity level, can be used multiple times.
.It Fl vector Ar 'x y z'
Append a vector parameter to be passed to
.Fn main Ns .
.It Fl float Ar number
Append a float parameter to be passed to
.Fn main Ns .
.It Fl string Ar 'text'
Append a string parameter to be passed to
.Fn main Ns .
.El
.Sh BUILTINS
This option disables execution.
.TP
.BI "-vector """ "x y z" """"
Append a vector parameter to be passed to \fImain\fR.
.TP
.BI "-float " number
Append a float parameter to be passed to \fImain\fR.
.TP
.BI "-string """ "text" """"
Append a string parameter to be passed to \fImain\fR.
.SH BUILTINS
The following builtin functions are available:
.Bl -ohang
.It Li 1) void print(string...) = #1;
.Bd -unfilled -offset indent -compact
.TP
.RI "1) " void " print(" string... ") = " "#1" ;
Print the passed strings to stdout. At most 8 strings are allowed.
.Ed
.It Li 2) string ftos(float) = #2;
.D1 Convert a float to a string.
.It Li 3) entity spawn() = #3;
.D1 Spawn an entity.
.It Li 4) void remove(entity) = #4;
.D1 Remove an entity.
.It Li 5) string vtos(vector) = #5;
.D1 Convert a vector to a string.
.It Li 6) void error(string...) = #6;
.D1 Print strings to stdout and then exit with an error (limited to 8 arguments)
.It Li 7) float vlen(vector) = #7;
.D1 Get the length of a vector.
.It Li 8) string etos(entity) = #8;
.D1 Get the entity ID as string.
.It Li 9) float stof(string) = #9;
.D1 Convert a string to a float.
.It Li 10) string strcat(string, string) = #10;
.D1 Concatenate two strings, returning a tempstring.
.It Li 11) float strcmp(string, string) = #11;
.Li 12) float strncmp(string, string, float) = #11;
.D1 Compare two strings. Returns the same as the corresponding C functions.
.It Li 12) vector normalize(vector) = #12;
.D1 Normalize a vector so its length is 1.
.It Li 13) float sqrt(float) = #13;
.D1 Get a value's square root.
.El
.Sh SEE ALSO
.Xr gmqcc 1
.Sh AUTHOR
See <http://graphitemaster.github.com/gmqcc>.
.Sh BUGS
.TP
.RI "2) " string " ftos(" float ") = " "#2" ;
Convert a float to a string.
.TP
.RI "3) " entity " spawn() = " "#3" ;
Spawn an entity.
.TP
.RI "4) " void " remove(" entity ") = " "#4" ;
Remove an entity.
.TP
.RI "5) " string " vtos(" vector ") = " "#5" ;
Convert a vector to a string.
.TP
.RI "6) " void " error(" string... ") = " "#6" ;
Print at most 8 strings to stdout and then exit with an error.
.TP
.RI "7) " float " vlen(" vector ") = " "#7" ;
Get the length of a vector.
.TP
.RI "8) " string " etos(" entity ") = " "#8" ;
Get the entity ID as string.
.TP
.RI "9) " float " stof(" string ") = " "#9" ;
Convert a string to a float.
.SH BUGS
Please report bugs on <http://github.com/graphitemaster/gmqcc/issues>,
or see <http://graphitemaster.github.com/gmqcc> on how to contact us.
.SH SEE ALSO
.IR gmqcc (1)
.SH AUTHOR
See <http://graphitemaster.github.com/gmqcc>.

File diff suppressed because it is too large Load diff

214
file.c Normal file
View file

@ -0,0 +1,214 @@
/*
* Copyright (C) 2012
* Dale Weiler
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "gmqcc.h"
/*
* This is essentially a "wrapper" interface around standard C's IO
* library. There is two reason we implement this, 1) visual studio
* hearts for "secure" varations, as part of it's "Security Enhancements
* in the CRT" (http://msdn.microsoft.com/en-us/library/8ef0s5kh.aspx).
* 2) But one of the greater reasons is for the possibility of large file
* support in the future. I don't expect to reach the 2GB limit any
* time soon (mainly because that would be insane). But when it comes
* to adding support for some other larger IO tasks (in the test-suite,
* or even the QCVM we'll need it). There is also a third possibility of
* building .dat files directly from zip files (which would be very cool
* at least I think so).
*/
#ifdef _MSC_VER
/* {{{ */
/*
* Visual Studio has security CRT features which I actually want to support
* if we ever port to Windows 8, and want GMQCC to be API safe.
*
* We handle them here, for all file-operations.
*/
static void file_exception (
const wchar_t *expression,
const wchar_t *function,
const wchar_t *file,
unsigned int line,
uintptr_t reserved
) {
wprintf(L"Invalid parameter dectected %s:%d %s [%s]\n", file, line, function, expression);
wprintf(L"Aborting ...\n");
abort();
}
static void file_init() {
static bool init = false;
if (init)
return;
_set_invalid_parameter_handler(&file_exception);
/*
* Turnoff the message box for CRT asserations otherwise
* we don't get the error reported to the console as we should
* otherwise get.
*/
_CrtSetReportMode(_CRT_ASSERT, 0);
init = !init;
}
FILE *file_open(const char *filename, const char *mode) {
FILE *handle = NULL;
file_init();
return ((fopen_s(&handle, filename, mode) != 0) ? NULL : handle;
}
size_t file_read(void *buffer, size_t size, size_t count, FILE *fp) {
file_init();
return fread_s(buffer, size*count, size, count, fp);
}
int file_printf(FILE *fp, const char *format, ...) {
int rt;
va_list va;
va_start(va, format);
file_init();
rt = vfprintf_s(fp, format, va);
va_end (va);
return rt;
}
/* }}} */
#else
/* {{{ */
/*
* All other compilers/platforms that don't restrict insane policies on
* IO for no aparent reason.
*/
FILE *file_open(const char *filename, const char *mode) {
return fopen(filename, mode);
}
size_t file_read(void *buffer, size_t size, size_t count, FILE *fp) {
return fread(buffer, size, count, fp);
}
int file_printf(FILE *fp, const char *format, ...) {
int rt;
va_list va;
va_start(va, format);
rt = vfprintf(fp, format, va);
va_end (va);
return rt;
}
/* }}} */
#endif
/*
* These are implemented as just generic wrappers to keep consistency in
* the API. Not as macros though
*/
void file_close(FILE *fp) {
/* Invokes file_exception on windows if fp is null */
fclose (fp);
}
size_t file_write (
const void *buffer,
size_t size,
size_t count,
FILE *fp
) {
/* Invokes file_exception on windows if fp is null */
return fwrite(buffer, size, count, fp);
}
int file_error(FILE *fp) {
/* Invokes file_exception on windows if fp is null */
return ferror(fp);
}
int file_getc(FILE *fp) {
/* Invokes file_exception on windows if fp is null */
return fgetc(fp);
}
int file_puts(FILE *fp, const char *str) {
/* Invokes file_exception on windows if fp is null */
return fputs(str, fp);
}
int file_seek(FILE *fp, long int off, int whence) {
/* Invokes file_exception on windows if fp is null */
return fseek(fp, off, whence);
}
/*
* Implements libc getline for systems that don't have it, which is
* assmed all. This works the same as getline().
*/
int file_getline(char **lineptr, size_t *n, FILE *stream) {
int chr;
int ret;
char *pos;
if (!lineptr || !n || !stream)
return -1;
if (!*lineptr) {
if (!(*lineptr = (char*)mem_a((*n=64))))
return -1;
}
chr = *n;
pos = *lineptr;
for (;;) {
int c = file_getc(stream);
if (chr < 2) {
*n += (*n > 16) ? *n : 64;
chr = *n + *lineptr - pos;
if (!(*lineptr = (char*)mem_r(*lineptr,*n)))
return -1;
pos = *n - chr + *lineptr;
}
if (ferror(stream))
return -1;
if (c == EOF) {
if (pos == *lineptr)
return -1;
else
break;
}
*pos++ = c;
chr--;
if (c == '\n')
break;
}
*pos = '\0';
return (ret = pos - *lineptr);
}

1679
fold.cpp

File diff suppressed because it is too large Load diff

121
fold.h
View file

@ -1,121 +0,0 @@
#ifndef GMQCC_FOLD_HDR
#define GMQCC_FOLD_HDR
#include "lexer.h"
#include "gmqcc.h"
struct ir_builder;
struct ir_value;
struct ast_function;
struct ast_ifthen;
struct ast_ternary;
struct ast_expression;
struct ast_value;
struct parser_t;
struct fold {
fold();
fold(parser_t *parser);
~fold();
// Bitmask describing which branches of a conditional to take after folding.
// Zero indicates all the branches can be removed.
// ON_TRUE means ON_FALSE can be removed.
// ON_FALSE means ON_TRUE can be removed.
// ON_TRUE | ON_FALSE means nothing can be removed.
enum {
ON_TRUE = 1 << 0,
ON_FALSE = 1 << 1,
};
bool generate(ir_builder *ir);
ast_expression *op(const oper_info *info, ast_expression **opexprs);
ast_expression *intrinsic(const char *intrinsic, size_t n_args, ast_expression **args);
static uint32_t cond_ternary(ast_value *condval, ast_ternary *branch);
static uint32_t cond_ifthen(ast_value *condval, ast_ifthen *branch);
static ast_expression *superfluous(ast_expression *left, ast_expression *right, int op);
static ast_expression *binary(lex_ctx_t ctx, int op, ast_expression *left, ast_expression *right);
ast_expression *constgen_float(qcfloat_t value, bool inexact);
ast_expression *constgen_vector(vec3_t value);
ast_expression *constgen_string(const char *str, bool translate);
ast_expression *constgen_string(const std::string &str, bool translate);
ast_value *imm_float(size_t index) const { return m_imm_float[index]; }
ast_value *imm_vector(size_t index) const { return m_imm_vector[index]; }
protected:
static qcfloat_t immvalue_float(ast_value *value);
static vec3_t immvalue_vector(ast_value *value);
static const char *immvalue_string(ast_value *value);
lex_ctx_t ctx();
bool immediate_true(ast_value *v);
bool check_except_float_impl(void (*callback)(void), ast_value *a, ast_value *b);
bool check_inexact_float(ast_value *a, ast_value *b);
ast_expression *op_mul_vec(vec3_t vec, ast_value *sel, const char *set);
ast_expression *op_neg(ast_value *a);
ast_expression *op_not(ast_value *a);
ast_expression *op_add(ast_value *a, ast_value *b);
ast_expression *op_sub(ast_value *a, ast_value *b);
ast_expression *op_mul(ast_value *a, ast_value *b);
ast_expression *op_div(ast_value *a, ast_value *b);
ast_expression *op_mod(ast_value *a, ast_value *b);
ast_expression *op_bor(ast_value *a, ast_value *b);
ast_expression *op_band(ast_value *a, ast_value *b);
ast_expression *op_xor(ast_value *a, ast_value *b);
ast_expression *op_lshift(ast_value *a, ast_value *b);
ast_expression *op_rshift(ast_value *a, ast_value *b);
ast_expression *op_andor(ast_value *a, ast_value *b, float expr);
ast_expression *op_tern(ast_value *a, ast_value *b, ast_value *c);
ast_expression *op_exp(ast_value *a, ast_value *b);
ast_expression *op_lteqgt(ast_value *a, ast_value *b);
ast_expression *op_ltgt(ast_value *a, ast_value *b, bool lt);
ast_expression *op_cmp(ast_value *a, ast_value *b, bool ne);
ast_expression *op_bnot(ast_value *a);
ast_expression *op_cross(ast_value *a, ast_value *b);
ast_expression *op_length(ast_value *a);
ast_expression *intrinsic_isfinite(ast_value *a);
ast_expression *intrinsic_isinf(ast_value *a);
ast_expression *intrinsic_isnan(ast_value *a);
ast_expression *intrinsic_isnormal(ast_value *a);
ast_expression *intrinsic_signbit(ast_value *a);
ast_expression *intrinsic_acosh(ast_value *a);
ast_expression *intrinsic_asinh(ast_value *a);
ast_expression *intrinsic_atanh(ast_value *a);
ast_expression *intrinsic_exp(ast_value *a);
ast_expression *intrinsic_exp2(ast_value *a);
ast_expression *intrinsic_expm1(ast_value *a);
ast_expression *intrinsic_pow(ast_value *lhs, ast_value *rhs);
ast_expression *intrinsic_mod(ast_value *lhs, ast_value *rhs);
ast_expression *intrinsic_fabs(ast_value *a);
ast_expression* intrinsic_nan(void);
ast_expression* intrinsic_epsilon(void);
ast_expression* intrinsic_inf(void);
static qcfloat_t immvalue_float(ir_value *value);
static vec3_t immvalue_vector(ir_value *value);
static uint32_t cond(ast_value *condval, ast_ifthen *branch);
private:
friend struct intrin;
std::vector<ast_value*> m_imm_float;
std::vector<ast_value*> m_imm_vector;
std::vector<ast_value*> m_imm_string;
hash_table_t *m_imm_string_untranslate; /* map<string, ast_value*> */
hash_table_t *m_imm_string_dotranslate; /* map<string, ast_value*> */
parser_t *m_parser;
bool m_initialized;
};
#endif

File diff suppressed because it is too large Load diff

1022
gmqcc.h

File diff suppressed because it is too large Load diff

View file

@ -1,745 +1,159 @@
#This configuration file is similar to a regular .ini file. Comments start
#with hashtags or semicolons, sections are written in square brackets and
#in each section there can be arbitrary many key-value pairs.
# This is an example INI file that can be used to set compiler options
# without the rquirement for supplying them as arguments on the command
# line, this can be coupled with progs.src. To utilize this file there
# are two options availble, if it's named "gmqcc.ini" or "gmqcc.cfg" and
# the file exists in the directory that GMQCC is invoked from, the compiler
# will implicitally find and execute regardless. For more freedom you may
# use -config=<file> from the command line to specify a more explicit
# directory/name.ext for the configuration file.
#There are 3 sections currently: flags, warnings, optimizations.
#They contain a list of boolean values of the form VARNAME = true or
#VARNAME = false. The variable names are the same as for the corre
#sponding -W, -f or -O flag written with only capital letters and dashes
#replaced by underscores.
# These are common compiler flags usually represented via the -f prefix
# from the command line.
[flags]
#Add some additional characters to the string table in order to
#compensate for a wrong boundcheck in some specific version of the
#darkplaces engine.
# Enabling this can potentially reduces code size by overlapping
# locals where possible.
OVERLAP_LOCALS = false
DARKPLACES_STRING_TABLE_BUG = true
# in some older versions of the Darkplaces engine the string table
# size is computed wrong causing compiled progs.dat to fail to load
# Enabling this works around the bug by writing a few additional
# null bytes to the end of the string table to compensate.
DARKPLACES_STRING_TABLE_BUG = false
# Enabling this corrects the assignment of vector field pointers via
# subsituting STORE_FLD with STORE_V.
ADJUST_VECTOR_FIELDS = true
#When assigning to field pointers of type .vector the common be
#haviour in compilers like fteqcc is to only assign the x-compo
#nent of the pointer. This means that you can use the vector as
#such, but you cannot use its y and z components directly. This
#flag fixes this behaviour. Before using it make sure your code
#does not depend on the buggy behaviour.
# Enabling this allows the use of the FTEQ preprocessor, as well as
# additional preprocessing directives such as #error and #warning.
FTEPP = true
ADJUST_VECTOR_FIELDS = true
# Enabling this relaxes switch statement semantics
RELAXED_SWITCH = false
# Enabling this allows short-circut evaluation and logic, opposed
# to full evaluation.
SHORT_LOGIC = false
#Enable a partially fteqcc-compatible preprocessor. It supports
#all the features used in the Xonotic codebase. If you need more,
#write a ticket.
# Enabling this allows perl-like evaluation/logic.
PERL_LOGIC = true
FTEPP = true
# Enabling this allows the use of the "translatable strings" extension
# assisted by .po files.
TRANSLATABLE_STRINGS = false
# Enabling this prevents initializations from becoming constant unless
# 'const' is specified as a type qualifier.
INITIALIZED_NONCONSTANTS = false
#Enable some predefined macros. This only works in combination
#with '-fftepp' and is currently not included by '-std=fteqcc'.
#The following macros will be added:
#
# __LINE__
# __FILE__
# __COUNTER__
# __COUNTER_LAST__
# __RANDOM__
# __RANDOM_LAST__
# __DATE__
# __TIME__
# __FUNC__
#
#Note that __FUNC__ is not actually a preprocessor macro, but is
#recognized by the parser even with the preprocessor disabled.
#
#Note that fteqcc also defines __NULL__ which becomes the first
#global. Assigning it to a vector does not yield the same result
#as in gmqcc where __NULL__ is defined to nil (See -funtyped-nil
#), which will cause the vector to be zero in all components. With
#fteqcc only the first component will be 0, while the other two
#will become the first to of the global return value. This behav
#ior is odd and relying on it should be discouraged, and thus is
#not supported by gmqcc.
# Enabling this allows function types to be assignable even if their
# signatures are invariant of each other.
ASSIGN_FUNCTION_TYPES = false
FTEPP_PREDEFS = false
# Enabling this will allow the generation of .lno files for engine
# virtual machine backtraces (this is enabled with -g as well).
LNO = false
# Enabling this corrects ternary percedence bugs present in fteqcc.
CORRECT_TERNARY = true
#Enable math constant definitions. This only works in combination
#with '-fftepp' and is currently not included by '-std=fteqcc'.
#The following macros will be added:
#
# M_E
# M_LOG2E
# M_LOG10E
# M_LN2
# M_LN10
# M_PI
# M_PI_2
# M_PI_4
# M_1_PI
# M_2_PI
# M_2_SQRTPI
# M_SQRT2
# M_SQRT1_2
# M_TAU
FTEPP_MATHDEFS = false
#Enable indirect macro expansion. This only works in combination
#with '-fftepp' and is currently not included by '-std=fteqcc'.
#Enabling this behavior will allow the preprocessor to operate more
#like the standard C preprocessor in that it will allow arguments
#of macros which are macro-expanded to be substituted into the
#definition of the macro. As an example:
#
# #define STR1(x) #x
# #define STR2(x) STR1(x)
# #define THE_ANSWER 42
# #define THE_ANSWER_STR STR2(THE_ANSWER) /* "42" */
#
#With this enabled, an expansion of THE_ANSWER_STR will yield
#the string "42". With this disabled an expansion of THE_ANSWER_STR
#will yield "THE_ANSWER"
FTEPP_INDIRECT_EXPANSION = false
#Allow switch cases to use non constant variables.
RELAXED_SWITCH = true
#Perform early out in logical AND and OR expressions. The final
#result will be either a 0 or a 1, see the next flag for more pos
#sibilities.
SHORT_LOGIC = true
#In many languages, logical expressions perform early out in a
#special way: If the left operand of an AND yeilds true, or the
#one of an OR yields false, the complete expression evaluates to
#the right side. Thus true && 5 evaluates to 5 rather than 1.
PERL_LOGIC = false
#Enable the underscore intrinsic: Using _("A string constant")
#will cause the string immediate to get a name with a "dotrans
#late_" prefix. The darkplaces engine recognizes these and trans
#lates them in a way similar to how gettext works.
TRANSLATABLE_STRINGS = true
#Don't implicitly convert initialized variables to constants. With
#this flag, the const keyword is required to make a constant.
INITIALIZED_NONCONSTANTS = false
#If this flag is not set, (and it is set by default in the qcc and
#fteqcc standards), assigning function pointers of mismatching
#signatures will result in an error rather than a warning.
ASSIGN_FUNCTION_TYPES = true
#Produce a linenumber file along with the output .dat file.
LNO = false
#Use C's operator precedence for ternary expressions. Unless your
#code depends on fteqcc-compatible behaviour, you'll want to use
#this option.
CORRECT_TERNARY = true
#Normally vectors generate 4 defs, once for the vector, and once
#for its components with _x, _y, _z suffixes. This option prevents
#components from being listed.
SINGLE_VECTOR_DEFS = true
#Most QC compilers translate if(a_vector) directly as an IF on
#the vector, which means only the x-component is checked. This
#option causes vectors to be cast to actual booleans via a NOT_V
#and, if necessary, a NOT_F chained to it.
#
# if (a_vector) // becomes
# if not(!a_vector)
# // likewise
# a = a_vector && a_float // becomes
# a = !!a_vector && a_float
CORRECT_LOGIC = true
#An empty string is considered to be true everywhere. The NOT_S
#instruction usually considers an empty string to be false, this
#option effectively causes the unary not in strings to use NOT_F
#instead.
TRUE_EMPTY_STRINGS = false
#An empty string is considered to be false everywhere. This means
#loops and if statements which depend on a string will perform a
#NOT_S instruction on the string before using it.
FALSE_EMPTY_STRINGS = true
#Enable utf8 characters. This allows utf-8 encoded character con
#stants, and escape sequence codepoints in the valid utf-8 range.
#Effectively enabling escape sequences like '\{x2211}'.
UTF8 = true
#When a warning is treated as an error, and this option is set
#(which it is by default), it is like any other error and will
#cause compilation to stop. When disabling this flag by using
#-fno-bail-on-werror, compilation will continue until the end, but
#no output is generated. Instead the first such error message's
#context is shown.
BAIL_ON_WERROR = false
#Allow loops to be labeled, and allow 'break' and 'continue' to
#take an optional label to decide which loop to actually jump out
#of or continue.
#
# for :outer (i = 0; i < n; ++i) {
# while (inner) {
# ...;
# if (something)
# continue outer;
# }
# }
LOOP_LABELS = true
#Adds a global named 'nil' which is of no type and can be assigned
#to anything. No typechecking will be performed on assignments.
#Assigning to it is forbidden, using it in any other kind of
#expression is also not allowed.
#
#Note that this is different from fteqcc's __NULL__: In fteqcc,
#__NULL__ maps to the integer written as '0i'. It's can be
#assigned to function pointers and integers, but it'll error about
#invalid instructions when assigning it to floats without enabling
#the FTE instruction set. There's also a bug which allows it to be
#assigned to vectors, for which the source will be the global at
#offset 0, meaning the vector's y and z components will contain
#the OFS_RETURN x and y components.#
#
#In that gmqcc the nil global is an actual global filled with
#zeroes, and can be assigned to anything including fields, vectors
#or function pointers, and they end up becoming zeroed.
UNTYPED_NIL = true
#Various effects, usually to weaken some conditions.
# with -funtyped-nil
# Allow local variables named nil. (This will not
# allow declaring a global of that name.)
PERMISSIVE = false
#Allow variadic parameters to be accessed by QC code. This can be
#achieved via the '...' function, which takes a parameter index
#and a typename.
#
#Example:
#
# void vafunc(string...count) {
# float i;
# for (i = 0; i < count; ++i)
# print(...(i, string), "\n");
# }
VARIADIC_ARGS = true
#Most Quake VMs, including the one from FTEQW or up till recently
#Darkplaces, do not cope well with vector instructions with over
#lapping input and output. This option will avoid producing such
#code.
LEGACY_VECTOR_MATHS = false
#Usually builtin-numbers are just immediate constants. With this
#flag expressions can be used, as long as they are compile-time
#constant.
#
#Example:
#
# void printA() = #1; // the usual way
# void printB() = #2-1; // with a constant expression
EXPRESSIONS_FOR_BUILTINS = true
#Enabiling this option will allow assigning values or expressions
#to the return keyword as if it were a local variable of the same
#type as the function's signature's return type.
#
#Example:
#
# float bar() { return 1024; }
# float fun() {
# return = bar();
# return; // returns value of bar (this can be omitted)
# }
RETURN_ASSIGNMENTS = true
#When passing on varargs to a different functions, this turns some
#static error cases into warnings. Like when the caller's varargs
#are restricted to a different type than the callee's parameter.
#Or a list of unrestricted varargs is passed into restricted
#varargs.
UNSAFE_VARARGS = false
#Always use STORE_F, LOAD_F, STOREP_F when accessing scalar variables.
#This is somewhat incorrect assembly instruction use, but in all engines
#they do exactly the same. This makes disassembly output harder to read,
#breaks decompilers, but causes the output file to be better compressible.
TYPELESS_STORES = false
#In commutative instructions, always put the lower-numbered operand first.
#This shaves off 1 byte of entropy from all these instructions, reducing
#compressed size of the output file.
SORT_OPERANDS = false
#Emulate OP_STATE operations in code rather than using the instruction.
#The desired fps can be set via -state-fps=NUM, defaults to 10.
EMULATE_STATE = false
#Turn on arithmetic exception tests in the compiler. In constant expressions
#which trigger exceptions like division by zero, overflow, underflow, etc,
#the following flag will produce diagnostics for what triggered that
#exception.
ARITHMETIC_EXCEPTIONS = false
#Split vector-literals which are only used dirctly as function parameters
#into 3 floats stored separately to reduce the number of globals at the
#expense of additional instructions.
SPLIT_VECTOR_PARAMETERS = false
#Force all expressions to be "eraseable" which permits the compiler
#to remove unused functions, variables and statements. This is
#equivlant to putting [[eraseable]] on all definitions. This is
#dangerous as it breaks auto cvars, definitions for functions the
#engine may be looking for and translatable strings. Instead, you
#can mark a definition with [[noerase]] to prevent this from happening.
DEFAULT_ERASEABLE = false
# These are all the warnings, usually present via the -W prefix from
# the command line.
[warnings]
#Generate a warning about variables which are declared but never
#used. This can be avoided by adding the noref keyword in front
#of the variable declaration. Additionally a complete section of
#unreferenced variables can be opened using #pragma noref 1 and
#closed via #pragma noref 0.
# ?? Used for debugging ??
DEBUG = false
UNUSED_VARIABLE = false
# Enables warnings about unused variables.
UNUSED_VARIABLE = true
# Enables warnings about uninitialized variables.
USED_UNINITIALIZED = true
#Generate a warning about vector variables which are declared but
#components of it are never used.
# Enables warnings about the unknown control sequences in the source
# stream.
UNKNOWN_CONTROL_SEQUENCE = true
UNUSED_COMPONENT = false
# Enables warnings about the use of (an) extension(s).
EXTENSIONS = true
#Generate a warning if it is possible that a variable can be used
#without prior initialization. Note that this warning is not nec
#essarily reliable if the initialization happens only under cer
#tain conditions. The other way is not possible: that the warning
#is not generated when uninitialized use is possible.
# Enables warnings about redeclared fields.
FIELD_REDECLARED = true
USED_UNINITIALIZED = false
# Enables warnings about missing return values.
MISSING_RETURN_VALUES = true
# Enables warnings about missing parameters for function calls.
TOO_FEW_PARAMETERS = true
#Generate an error when an unrecognized control sequence in a
#string is used. Meaning: when there's a character after a back
#slash in a string which has no known meaning.
# Enables warnings about locals shadowing parameters or other locals.
LOCAL_SHADOWS = true
UNKNOWN_CONTROL_SEQUENCE = false
# Enables warnings about constants specified as locals.
LOCAL_CONSTANTS = true
# Enables warnings about variables declared as type void.
VOID_VARIABLES = true
#Warn when using special extensions which are not part of the
#selected standard.
# Enables warnings about implicitally declared function pointers.
IMPLICIT_FUNCTION_POINTER = true
EXTENSIONS = false
# Enables warnings for use of varadics for non-builtin functions.
VARIADIC_FUNCTION = true
# Enables warnings about duplicated frame macros.
FRAME_MACROS = true
#Generally QC compilers ignore redeclaration of fields. Here you
#can optionally enable a warning.
# Enables warnings about effectivless statements.
EFFECTLESS_STATEMENT = true
FIELD_REDECLARED = false
# Enables warnings of "end_sys_fields" beiing declared a field.
END_SYS_FIELDS = true
# Enables warnings for infompatible function pointer signatures used
# in assignment.
ASSIGN_FUNCTION_TYPES = true
#Functions which aren't of type void will warn if it possible to
#reach the end without returning an actual value.
# Enables warnings about redefined macros in the preprocessor
PREPROCESSOR = true
MISSING_RETURN_VALUES = false
# Enables warnings about multi-file if statements
MULTIFILE_IF = true
# Enables warnings about double declarations
DOUBLE_DECLARATION = true
#Warn about a function call with an invalid number of parameters.
# Enables warnings about type qualifiers containing both 'var' and
# 'const'
CONST_VAR = true
INVALID_PARAMETER_COUNT = false
# Enables warnings about the use of multibytes characters / constants
MULTIBYTE_CHARACTER = true
# Enables warnings about ternary expressions whos precedence may be
# not what was initially expected.
TERNARY_PRECEDENCE = true
#Warn when a locally declared variable shadows variable.
# Enables warnings about unknown pragmas.
UNKNOWN_PRAGMAS = true
LOCAL_SHADOWS = false
#Warn when the initialization of a local variable turns the vari
#able into a constant. This is default behaviour unless
#-finitialized-nonconstants is used.
LOCAL_CONSTANTS = false
#There are only 2 known global variables of type void:
#end_sys_globals and end_sys_fields. Any other void-variable
#will warn.
VOID_VARIABLES = false
#A global function which is not declared with the var keyword is
#expected to have an implementing body, or be a builtin. If nei
#ther is the case, it implicitly becomes a function pointer, and a
#warning is generated.
IMPLICIT_FUNCTION_POINTER = false
#Currently there's no way for an in QC implemented function to
#access variadic parameters. If a function with variadic parame
#ters has an implementing body, a warning will be generated.
VARIADIC_FUNCTION = false
#Generate warnings about $frame commands, for instance about
#duplicate frame definitions.
FRAME_MACROS = false
#Warn about statements which have no effect. Any expression which
#does not call a function or assigns a variable.
EFFECTLESS_STATEMENT = false
#The end_sys_fields variable is supposed to be a global variable
#of type void. It is also recognized as a field but this will
#generate a warning.
END_SYS_FIELDS = false
#Warn when assigning to a function pointer with an unmatching sig
#nature. This usually happens in cases like assigning the null
#function to an entity's .think function pointer.
ASSIGN_FUNCTION_TYPES = false
#Show warnings created using the preprocessor's '#warning' directive
CPP = true
#Warn if there's a preprocessor #if spanning across several files.
MULTIFILE_IF = true
#Warn about multiple declarations of globals. This seems pretty
#common in QC code so you probably do not want this unless you
#want to clean up your code.
DOUBLE_DECLARATION = false
#The combination of const and var is not illegal, however differ
#ent compilers may handle them differently. We were told, the
#intention is to create a function-pointer which is not assigna
#ble. This is exactly how we interpret it. However for this
#interpretation the var keyword is considered superfluous (and
#philosophically wrong), so it is possible to generate a warning
#about this.
CONST_VAR = true
#Warn about multibyte character constants, they do not work right
#now.
MULTIBYTE_CHARACTER = false
#Warn if a ternary expression which contains a comma operator is
#used without enclosing parenthesis, since this is most likely not
#what you actually want. We recommend the -fcorrect-ternary
#option.
TERNARY_PRECEDENCE = false
#Warn when encountering an unrecognized #pragma line.
UNKNOWN_PRAGMAS = true
#Warn about unreachable code. That is: code after a return state
#ment, or code after a call to a function marked as 'noreturn'.
UNREACHABLE_CODE = true
#Enable some warnings added in order to help debugging in the com
#piler. You won't need this.
DEBUG = false
#Warn on an unknown attribute. The warning will inlclude only the
#first token inside the enclosing attribute-brackets. This may
#change when the actual attribute syntax is better defined.
UNKNOWN_ATTRIBUTE = true
#Warn when using reserved names such as nil.
RESERVED_NAMES = true
#Warn about global constants (using the const keyword) with no
#assigned value.
UNINITIALIZED_CONSTANT = true
#Warn about global variables with no initializing value. This is
#off by default, and is added mostly to help find null-values
#which are supposed to be replaced by the untyped 'nil' constant.
UNINITIALIZED_GLOBAL = true
#Warn when a variables is redeclared with a different qualifier.
#For example when redeclaring a variable as 'var' which was previ
#ously marked 'const'.
DIFFERENT_QUALIFIERS = true
#Similar to the above but for attributes like [[noreturn]].
DIFFERENT_ATTRIBUTES = true
#Warn when a function is marked with the attribute "[[depre
#cated]]". This flag enables a warning on calls to functions
#marked as such.
DEPRECATED = true
#Warn about possible mistakes caused by missing or wrong parenthe
#sis, like an assignment in an 'if' condition when there's no
#additional set of parens around the assignment.
PARENTHESIS = true
#When passing variadic parameters via ...(N) it can happen that
#incompatible types are passed to functions. This enables several
#warnings when static typechecking cannot guarantee consistent
#behavior.
UNSAFE_TYPES = true
#When compiling original id1 QC there is a definition for `break`
#which conflicts with the 'break' keyword in GMQCC. Enabling this
#print a warning when the definition occurs. The definition is
#ignored for both cases.
BREAKDEF = true
#When compiling original QuakeWorld QC there are instances where
#code overwrites constants. This is considered an error, however
#for QuakeWorld to compile it needs to be treated as a warning
#instead, as such this warning only works when -std=qcc.
CONST_OVERWRITE = true
#Warn about the use of preprocessor directives inside macros.
DIRECTIVE_INMACRO = true
#When using a function that is not explicitly defined, the compiler
#will search its intrinsics table for something that matches that
#function name by appending "__builtin_" to it. This behaviour may
#be unexpected, so enabling this will produce a diagnostic when
#such a function is resolved to a builtin.
BUILTINS = true
#When comparing an inexact value such as `1.0/3.0' the result is
#pathologically wrong. Enabling this will trigger a compiler warning
#on such expressions.
INEXACT_COMPARES = true
# Enables warnings about unreachable code.
UNREACHABLE_CODE = true
# Enables preprocessor "#warnings"
CPP = true
# Finally these are all the optimizations, usually present via the -O
# prefix from the command line.
[optimizations]
#Some general peephole optimizations. For instance the code `a = b
#+ c` typically generates 2 instructions, an ADD and a STORE. This
#optimization removes the STORE and lets the ADD write directly
#into A.
# Enables peephole optimizations.
PEEPHOLE = true
PEEPHOLE = true
# Enables localtemp omission optimizations.
LOCALTEMPS = true
# Enables tail recrusion optimizationd.
TAIL_RECURSION = true
#Tail recursive function calls will be turned into loops to avoid
#the overhead of the CALL and RETURN instructions.
TAIL_RECURSION = true
#Make all functions which use neither local arrays nor have locals
#which are seen as possibly uninitialized use the same local sec
#tion. This should be pretty safe compared to other compilers
#which do not check for uninitialized values properly. The problem
#is that there's QC code out there which really doesn't initialize
#some values. This is fine as long as this kind of optimization
#isn't used, but also, only as long as the functions cannot be
#called in a recursive manner. Since it's hard to know whether or
#not an array is actually fully initialized, especially when ini
#tializing it via a loop, we assume functions with arrays to be
#too dangerous for this optimization.
OVERLAP_LOCALS = true
#This promotes locally declared variables to "temps". Meaning when
#a temporary result of an operation has to be stored somewhere, a
#local variable which is not 'alive' at that point can be used to
#keep the result. This can reduce the size of the global section.
#This will not have declared variables overlap, even if it was
#possible.
LOCAL_TEMPS = true
#Causes temporary values which do not need to be backed up on a
#CALL to not be stored in the function's locals-area. With this, a
#CALL to a function may need to back up fewer values and thus exe
#cute faster.
GLOBAL_TEMPS = true
#Don't generate defs for immediate values or even declared con
#stants. Meaning variables which are implicitly constant or qual
#ified as such using the 'const' keyword.
STRIP_CONSTANT_NAMES = true
#Aggressively reuse strings in the string section. When a string
#should be added which is the trailing substring of an already
#existing string, the existing string's tail will be returned
#instead of the new string being added.
#
#For example the following code will only generate 1 string:
#
# print("Hello you!\n");
# print("you!\n"); // trailing substring of "Hello you!\n"
#
#There's however one limitation. Strings are still processed in
#order, so if the above print statements were reversed, this opti
#mization would not happen.
OVERLAP_STRINGS = true
#By default, all parameters of a CALL are copied into the parame
#ter-globals right before the CALL instructions. This is the easi
#est and safest way to translate calls, but also adds a lot of
#unnecessary copying and unnecessary temporary values. This opti
#mization makes operations which are used as a parameter evaluate
#directly into the parameter-global if that is possible, which is
#when there's no other CALL instruction in between.
CALL_STORES = true
#Usually an empty RETURN instruction is added to the end of a void
#typed function. However, additionally after every function a DONE
#instruction is added for several reasons. (For example the qcvm's
#disassemble switch uses it to know when the function ends.). This
#optimization replaces that last RETURN with DONE rather than
#adding the DONE additionally.
VOID_RETURN = true
#Because traditional QC code doesn't allow you to access individ
#ual vector components of a computed vector without storing it in
#a local first, sometimes people multiply it by a constant like
#'0 1 0' to get, in this case, the y component of a vector. This
#optimization will turn such a multiplication into a direct compo
#nent access. If the factor is anything other than 1, a float-mul
#tiplication will be added, which is still faster than a vector
#multiplication.
VECTOR_COMPONENTS = true
#For constant expressions that result in dead code (such as a
#branch whos condition can be evaluated at compile-time), this
#will eliminate the branch and else body (if present) to produce
#more optimal code.
CONST_FOLD_DCE = true
#For constant expressions we can fold them to immediate values.
#this option cannot be disabled or enabled, the compiler forces
#it to stay enabled by ignoring the value entierly. There are
#plans to enable some level of constant fold disabling, but right
#now the language can't function without it. This is merley here
#as an exercise to the reader.
CONST_FOLD = true
# Enables tail-call optimizations.
TAIL_CALLS = true

2048
intrin.cpp

File diff suppressed because it is too large Load diff

View file

@ -1,74 +0,0 @@
#ifndef GMQCC_INTRIN_HDR
#define GMQCC_INTRIN_HDR
#include "gmqcc.h"
struct fold;
struct parser_t;
struct ast_function;
struct ast_expression;
struct ast_value;
struct intrin;
struct intrin_func_t {
ast_expression *(intrin::*function)();
const char *name;
const char *alias;
size_t args;
};
struct intrin {
intrin() = default;
intrin(parser_t *parser);
ast_expression *debug_typestring();
ast_expression *do_fold(ast_value *val, ast_expression **exprs);
ast_expression *func_try(size_t offset, const char *compare);
ast_expression *func_self(const char *name, const char *from);
ast_expression *func(const char *name);
protected:
lex_ctx_t ctx() const;
ast_function *value(ast_value **out, const char *name, qc_type vtype);
void reg(ast_value *const value, ast_function *const func);
ast_expression *nullfunc();
ast_expression *isfinite_();
ast_expression *isinf_();
ast_expression *isnan_();
ast_expression *isnormal_();
ast_expression *signbit_();
ast_expression *acosh_();
ast_expression *asinh_();
ast_expression *atanh_();
ast_expression *exp_();
ast_expression *exp2_();
ast_expression *expm1_();
ast_expression *pow_();
ast_expression *mod_();
ast_expression *fabs_();
ast_expression *epsilon_();
ast_expression *nan_();
ast_expression *inf_();
ast_expression *ln_();
ast_expression *log_variant(const char *name, float base);
ast_expression *log_();
ast_expression *log10_();
ast_expression *log2_();
ast_expression *logb_();
ast_expression *shift_variant(const char *name, size_t instr);
ast_expression *lshift();
ast_expression *rshift();
void error(const char *fmt, ...);
private:
parser_t *m_parser;
fold *m_fold;
std::vector<intrin_func_t> m_intrinsics;
std::vector<ast_expression*> m_generated;
};
#endif

3818
ir.c Normal file

File diff suppressed because it is too large Load diff

4094
ir.cpp

File diff suppressed because it is too large Load diff

504
ir.h
View file

@ -1,334 +1,336 @@
/*
* Copyright (C) 2012
* Wolfgang Bumiller
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef GMQCC_IR_HDR
#define GMQCC_IR_HDR
#include "gmqcc.h"
/*
* Type large enough to hold all the possible IR flags. This should be
* changed if the static assertion at the end of this file fails.
*/
typedef uint8_t ir_flag_t;
/* ir_value */
struct ir_value;
struct ir_instr;
struct ir_block;
struct ir_function;
struct ir_builder;
struct ir_life_entry_t {
typedef struct
{
/* both inclusive */
size_t start;
size_t end;
};
} ir_life_entry_t;
enum {
IR_FLAG_HAS_ARRAYS = 1 << 0,
IR_FLAG_HAS_UNINITIALIZED = 1 << 1,
IR_FLAG_HAS_GOTO = 1 << 2,
IR_FLAG_INCLUDE_DEF = 1 << 3,
IR_FLAG_ERASABLE = 1 << 4,
IR_FLAG_BLOCK_COVERAGE = 1 << 5,
IR_FLAG_NOREF = 1 << 6,
IR_FLAG_SPLIT_VECTOR = 1 << 7,
struct ir_function_s;
typedef struct ir_value_s {
char *name;
int vtype;
int store;
lex_ctx context;
/* even the IR knows the subtype of a field */
int fieldtype;
/* and the output type of a function */
int outtype;
/* 'const' vs 'var' qualifier */
int cvq;
IR_FLAG_LAST,
IR_FLAG_MASK_NO_OVERLAP = (IR_FLAG_HAS_ARRAYS | IR_FLAG_HAS_UNINITIALIZED),
IR_FLAG_MASK_NO_LOCAL_TEMPS = (IR_FLAG_HAS_ARRAYS | IR_FLAG_HAS_UNINITIALIZED)
};
struct ir_instr_s **reads;
struct ir_instr_s **writes;
struct ir_value {
ir_value(std::string&& name, store_type storetype, qc_type vtype);
ir_value(ir_function *owner, std::string&& name, store_type storetype, qc_type vtype);
~ir_value();
ir_value *vectorMember(unsigned int member);
bool GMQCC_WARN setFloat(float);
bool GMQCC_WARN setFunc(int);
bool GMQCC_WARN setString(const char*);
bool GMQCC_WARN setVector(vec3_t);
bool GMQCC_WARN setField(ir_value*);
#if 0
bool GMQCC_WARN setInt(int);
#endif
bool lives(size_t at);
void dumpLife(int (*oprintf)(const char*, ...)) const;
void setCodeAddress(int32_t gaddr);
int32_t codeAddress() const;
bool insertLife(size_t idx, ir_life_entry_t);
bool setAlive(size_t position);
bool mergeLife(const ir_value *other);
std::string m_name;
qc_type m_vtype;
store_type m_store;
lex_ctx_t m_context;
qc_type m_fieldtype; // even the IR knows the subtype of a field
qc_type m_outtype; // and the output type of a function
int m_cvq; // 'const' vs 'var' qualifier
ir_flag_t m_flags;
std::vector<ir_instr *> m_reads;
std::vector<ir_instr *> m_writes;
// constant values
bool m_hasvalue;
/* constantvalues */
bool hasvalue;
union {
qcfloat_t vfloat;
int vint;
vec3_t vvec;
int32_t ivec[3];
char *vstring;
ir_value *vpointer;
ir_function *vfunc;
} m_constval;
float vfloat;
int vint;
vector vvec;
int32_t ivec[3];
char *vstring;
struct ir_value_s *vpointer;
struct ir_function_s *vfunc;
} constval;
struct {
int32_t globaladdr;
int32_t name;
int32_t local; // filled by the local-allocator
int32_t addroffset; // added for members
int32_t fieldaddr; // to generate field-addresses early
} m_code;
/* filled by the local-allocator */
int32_t local;
/* added for members */
int32_t addroffset;
/* to generate field-addresses early */
int32_t fieldaddr;
} code;
// for accessing vectors
ir_value *m_members[3];
ir_value *m_memberof;
/* for acessing vectors */
struct ir_value_s *members[3];
struct ir_value_s *memberof;
bool m_unique_life; // arrays will never overlap with temps
bool m_locked; // temps living during a CALL must be locked
bool m_callparam;
/* arrays will never overlap with temps */
bool unique_life;
std::vector<ir_life_entry_t> m_life; // For the temp allocator
/* For the temp allocator */
ir_life_entry_t *life;
} ir_value;
size_t size() const;
int32_t ir_value_code_addr(const ir_value*);
void dump(int (*oprintf)(const char*, ...)) const;
};
/* ir_value can be a variable, or created by an operation */
ir_value* ir_value_var(const char *name, int st, int vtype);
/* if a result of an operation: the function should store
* it to remember to delete it / garbage collect it
*/
ir_value* ir_value_out(struct ir_function_s *owner, const char *name, int st, int vtype);
void ir_value_delete(ir_value*);
bool ir_value_set_name(ir_value*, const char *name);
ir_value* ir_value_vector_member(ir_value*, unsigned int member);
bool GMQCC_WARN vec_ir_value_find(ir_value **vec, ir_value *what, size_t *idx);
bool GMQCC_WARN ir_value_set_float(ir_value*, float f);
bool GMQCC_WARN ir_value_set_func(ir_value*, int f);
#if 0
bool GMQCC_WARN ir_value_set_int(ir_value*, int i);
#endif
bool GMQCC_WARN ir_value_set_string(ir_value*, const char *s);
bool GMQCC_WARN ir_value_set_vector(ir_value*, vector v);
bool GMQCC_WARN ir_value_set_field(ir_value*, ir_value *fld);
/*bool ir_value_set_pointer_v(ir_value*, ir_value* p); */
/*bool ir_value_set_pointer_i(ir_value*, int i); */
/* merge an instruction into the life-range */
/* returns false if the lifepoint was already known */
bool ir_value_life_merge(ir_value*, size_t);
bool ir_value_life_merge_into(ir_value*, const ir_value*);
/* check if a value lives at a specific point */
bool ir_value_lives(ir_value*, size_t);
/* check if the life-range of 2 values overlaps */
bool ir_values_overlap(const ir_value*, const ir_value*);
void ir_value_dump(ir_value*, int (*oprintf)(const char*,...));
void ir_value_dump_life(const ir_value *self, int (*oprintf)(const char*,...));
/* PHI data */
struct ir_phi_entry_t {
ir_value *value;
ir_block *from;
};
typedef struct ir_phi_entry_s
{
ir_value *value;
struct ir_block_s *from;
} ir_phi_entry_t;
/* instruction */
struct ir_instr {
ir_instr(lex_ctx_t, ir_block *owner, int opcode);
~ir_instr();
typedef struct ir_instr_s
{
int opcode;
lex_ctx context;
ir_value* (_ops[3]);
struct ir_block_s* (bops[2]);
int m_opcode;
lex_ctx_t m_context;
ir_value *(_m_ops[3]) = { nullptr, nullptr, nullptr };
ir_block *(m_bops[2]) = { nullptr, nullptr };
std::vector<ir_phi_entry_t> m_phi;
std::vector<ir_value *> m_params;
// For the temp-allocation
size_t m_eid = 0;
// For IFs
bool m_likely = true;
ir_block *m_owner;
};
/* block */
struct ir_block {
ir_block(ir_function *owner, const std::string& name);
~ir_block();
ir_function *m_owner;
std::string m_label;
lex_ctx_t m_context;
bool m_final = false; /* once a jump is added we're done */
std::vector<ir_instr *> m_instr;
std::vector<ir_block *> m_entries;
std::vector<ir_block *> m_exits;
std::vector<ir_value *> m_living;
ir_phi_entry_t *phi;
ir_value **params;
/* For the temp-allocation */
size_t m_entry_id = 0;
size_t m_eid = 0;
bool m_is_return = false;
size_t eid;
bool m_generated = false;
size_t m_code_start = 0;
};
/* For IFs */
bool likely;
ir_value* ir_block_create_binop(ir_block*, lex_ctx_t, const char *label, int op, ir_value *left, ir_value *right);
ir_value* ir_block_create_unary(ir_block*, lex_ctx_t, const char *label, int op, ir_value *operand);
bool GMQCC_WARN ir_block_create_store_op(ir_block*, lex_ctx_t, int op, ir_value *target, ir_value *what);
bool GMQCC_WARN ir_block_create_storep(ir_block*, lex_ctx_t, ir_value *target, ir_value *what);
ir_value* ir_block_create_load_from_ent(ir_block*, lex_ctx_t, const char *label, ir_value *ent, ir_value *field, qc_type outype);
ir_value* ir_block_create_fieldaddress(ir_block*, lex_ctx_t, const char *label, ir_value *entity, ir_value *field);
bool GMQCC_WARN ir_block_create_state_op(ir_block*, lex_ctx_t, ir_value *frame, ir_value *think);
struct ir_block_s *owner;
} ir_instr;
ir_instr* ir_instr_new(lex_ctx ctx, struct ir_block_s *owner, int opcode);
void ir_instr_delete(ir_instr*);
bool GMQCC_WARN vec_ir_instr_find(ir_instr **vec, ir_instr *what, size_t *idx);
bool GMQCC_WARN ir_instr_op(ir_instr*, int op, ir_value *value, bool writing);
void ir_instr_dump(ir_instr* in, char *ind, int (*oprintf)(const char*,...));
/* block */
typedef struct ir_block_s
{
char *label;
lex_ctx context;
bool final; /* once a jump is added we're done */
ir_instr **instr;
struct ir_block_s **entries;
struct ir_block_s **exits;
ir_value **living;
/* For the temp-allocation */
size_t eid;
bool is_return;
size_t run_id;
struct ir_function_s *owner;
bool generated;
size_t code_start;
} ir_block;
ir_block* ir_block_new(struct ir_function_s *owner, const char *label);
void ir_block_delete(ir_block*);
bool ir_block_set_label(ir_block*, const char *label);
ir_value* ir_block_create_binop(ir_block*, lex_ctx, const char *label, int op,
ir_value *left, ir_value *right);
ir_value* ir_block_create_unary(ir_block*, lex_ctx, const char *label, int op,
ir_value *operand);
bool GMQCC_WARN ir_block_create_store_op(ir_block*, lex_ctx, int op, ir_value *target, ir_value *what);
bool GMQCC_WARN ir_block_create_store(ir_block*, lex_ctx, ir_value *target, ir_value *what);
bool GMQCC_WARN ir_block_create_storep(ir_block*, lex_ctx, ir_value *target, ir_value *what);
/* field must be of TYPE_FIELD */
ir_value* ir_block_create_load_from_ent(ir_block*, lex_ctx, const char *label, ir_value *ent, ir_value *field, int outype);
ir_value* ir_block_create_fieldaddress(ir_block*, lex_ctx, const char *label, ir_value *entity, ir_value *field);
/* This is to create an instruction of the form
* <outtype>%label := opcode a, b
*/
ir_instr* ir_block_create_phi(ir_block*, lex_ctx_t, const char *label, qc_type vtype);
ir_value* ir_block_create_general_instr(ir_block *self, lex_ctx, const char *label,
int op, ir_value *a, ir_value *b, int outype);
ir_value* ir_block_create_add(ir_block*, lex_ctx, const char *label, ir_value *l, ir_value *r);
ir_value* ir_block_create_sub(ir_block*, lex_ctx, const char *label, ir_value *l, ir_value *r);
ir_value* ir_block_create_mul(ir_block*, lex_ctx, const char *label, ir_value *l, ir_value *r);
ir_value* ir_block_create_div(ir_block*, lex_ctx, const char *label, ir_value *l, ir_value *r);
ir_instr* ir_block_create_phi(ir_block*, lex_ctx, const char *label, int vtype);
ir_value* ir_phi_value(ir_instr*);
void ir_phi_add(ir_instr*, ir_block *b, ir_value *v);
ir_instr* ir_block_create_call(ir_block*, lex_ctx_t, const char *label, ir_value *func, bool noreturn);
ir_instr* ir_block_create_call(ir_block*, lex_ctx, const char *label, ir_value *func, bool noreturn);
ir_value* ir_call_value(ir_instr*);
void ir_call_param(ir_instr*, ir_value*);
bool GMQCC_WARN ir_block_create_return(ir_block*, lex_ctx_t, ir_value *opt_value);
bool GMQCC_WARN ir_block_create_return(ir_block*, lex_ctx, ir_value *opt_value);
bool GMQCC_WARN ir_block_create_if(ir_block*, lex_ctx_t, ir_value *cond,
bool GMQCC_WARN ir_block_create_if(ir_block*, lex_ctx, ir_value *cond,
ir_block *ontrue, ir_block *onfalse);
/*
* A 'goto' is an actual 'goto' coded in QC, whereas
/* A 'goto' is an actual 'goto' coded in QC, whereas
* a 'jump' is a virtual construct which simply names the
* next block to go to.
* A goto usually becomes an OP_GOTO in the resulting code,
* whereas a 'jump' usually doesn't add any actual instruction.
*/
bool GMQCC_WARN ir_block_create_jump(ir_block*, lex_ctx_t, ir_block *to);
bool GMQCC_WARN ir_block_create_goto(ir_block*, lex_ctx_t, ir_block *to);
bool GMQCC_WARN ir_block_create_jump(ir_block*, lex_ctx, ir_block *to);
bool GMQCC_WARN ir_block_create_goto(ir_block*, lex_ctx, ir_block *to);
void ir_block_dump(ir_block*, char *ind, int (*oprintf)(const char*,...));
/* function */
struct ir_function {
ir_function(ir_builder *owner, qc_type returntype);
~ir_function();
ir_builder *m_owner;
typedef struct ir_function_s
{
char *name;
int outtype;
int *params;
ir_block **blocks;
std::string m_name;
qc_type m_outtype;
std::vector<int> m_params;
ir_flag_t m_flags = 0;
int m_builtin = 0;
int builtin;
std::vector<std::unique_ptr<ir_block>> m_blocks;
ir_value *value;
/*
* values generated from operations
/* values generated from operations
* which might get optimized away, so anything
* in there needs to be deleted in the dtor.
*/
std::vector<std::unique_ptr<ir_value>> m_values;
std::vector<std::unique_ptr<ir_value>> m_locals; /* locally defined variables */
ir_value *m_value = nullptr;
ir_value **values;
size_t m_allocated_locals = 0;
size_t m_globaltemps = 0;
/* locally defined variables */
ir_value **locals;
ir_block* m_first = nullptr;
ir_block* m_last = nullptr;
size_t allocated_locals;
lex_ctx_t m_context;
ir_block* first;
ir_block* last;
/*
* for prototypes - first we generate all the
lex_ctx context;
/* for prototypes - first we generate all the
* globals, and we remember teh function-defs
* so we can later fill in the entry pos
*
* remember the ID:
*/
qcint_t m_code_function_def = -1;
qcint code_function_def;
/* for temp allocation */
size_t m_run_id = 0;
size_t run_id;
/* vararg support: */
size_t m_max_varargs = 0;
};
struct ir_builder_s *owner;
} ir_function;
ir_function* ir_function_new(struct ir_builder_s *owner, int returntype);
void ir_function_delete(ir_function*);
void ir_function_collect_value(ir_function*, ir_value *value);
bool ir_function_set_name(ir_function*, const char *name);
ir_value* ir_function_create_local(ir_function *self, const char *name, int vtype, bool param);
ir_value* ir_function_create_local(ir_function *self, const std::string& name, qc_type vtype, bool param);
bool GMQCC_WARN ir_function_finalize(ir_function*);
ir_block* ir_function_create_block(lex_ctx_t ctx, ir_function*, const char *label);
/*
bool ir_function_naive_phi(ir_function*);
bool ir_function_enumerate(ir_function*);
bool ir_function_calculate_liferanges(ir_function*);
*/
ir_block* ir_function_create_block(lex_ctx ctx, ir_function*, const char *label);
void ir_function_dump(ir_function*, char *ind, int (*oprintf)(const char*,...));
/* builder */
#define IR_HT_SIZE 1024
#define IR_MAX_VINSTR_TEMPS 2
#define IR_HT_SIZE 1024
typedef struct ir_builder_s
{
char *name;
ir_function **functions;
ir_value **globals;
ir_value **fields;
struct ir_builder {
ir_builder(const std::string& modulename);
~ir_builder();
ht htfunctions;
ht htglobals;
ht htfields;
ir_function *createFunction(const std::string &name, qc_type outtype);
ir_value *createGlobal(const std::string &name, qc_type vtype);
ir_value *createField(const std::string &name, qc_type vtype);
ir_value *get_va_count();
bool generate(const char *filename);
void dump(int (*oprintf)(const char*, ...)) const;
ir_value **extparams;
ir_value *generateExtparamProto();
void generateExtparam();
const char **filenames;
qcint *filestrings;
/* we cache the #IMMEDIATE string here */
qcint str_immediate;
} ir_builder;
ir_value *literalFloat(float value, bool add_to_list);
ir_builder* ir_builder_new(const char *modulename);
void ir_builder_delete(ir_builder*);
std::string m_name;
std::vector<std::unique_ptr<ir_function>> m_functions;
std::vector<std::unique_ptr<ir_value>> m_globals;
std::vector<std::unique_ptr<ir_value>> m_fields;
// for reusing them in vector-splits, TODO: sort this or use a radix-tree
std::vector<ir_value*> m_const_floats;
bool ir_builder_set_name(ir_builder *self, const char *name);
ht m_htfunctions;
ht m_htglobals;
ht m_htfields;
ir_function* ir_builder_get_function(ir_builder*, const char *fun);
ir_function* ir_builder_create_function(ir_builder*, const char *name, int outtype);
// extparams' ir_values reference the ones from extparam_protos
std::vector<std::unique_ptr<ir_value>> m_extparam_protos;
std::vector<ir_value*> m_extparams;
ir_value* ir_builder_get_global(ir_builder*, const char *fun);
ir_value* ir_builder_create_global(ir_builder*, const char *name, int vtype);
ir_value* ir_builder_get_field(ir_builder*, const char *fun);
ir_value* ir_builder_create_field(ir_builder*, const char *name, int vtype);
// the highest func->allocated_locals
size_t m_max_locals = 0;
size_t m_max_globaltemps = 0;
uint32_t m_first_common_local = 0;
uint32_t m_first_common_globaltemp = 0;
bool ir_builder_generate(ir_builder *self, const char *filename);
std::vector<const char*> m_filenames;
std::vector<qcint_t> m_filestrings;
void ir_builder_dump(ir_builder*, int (*oprintf)(const char*, ...));
// we cache the #IMMEDIATE string here
qcint_t m_str_immediate = 0;
// there should just be this one nil
ir_value *m_nil;
ir_value *m_reserved_va_count = nullptr;
ir_value *m_coverage_func = nullptr;
/* some virtual instructions require temps, and their code is isolated
* so that we don't need to keep track of their liveness.
*/
ir_value *m_vinstr_temp[IR_MAX_VINSTR_TEMPS];
/* code generator */
std::unique_ptr<code_t> m_code;
private:
qcint_t filestring(const char *filename);
bool generateGlobal(ir_value*, bool is_local);
bool generateGlobalFunction(ir_value*);
bool generateGlobalFunctionCode(ir_value*);
bool generateFunctionLocals(ir_value*);
};
/*
* This code assumes 32 bit floats while generating binary
* Blub: don't use extern here, it's annoying and shows up in nm
* for some reason :P
*/
typedef int static_assert_is_32bit_float [(sizeof(int32_t) == 4) ? 1 : -1];
typedef int static_assert_is_32bit_integer[(sizeof(qcfloat_t) == 4) ? 1 : -1];
/*
* If the condition creates a situation where this becomes -1 size it means there are
* more IR_FLAGs than the type ir_flag_t is capable of holding. So either eliminate
* the IR flag count or change the ir_flag_t typedef to a type large enough to accomodate
* all the flags.
*/
typedef int static_assert_is_ir_flag_safe [((IR_FLAG_LAST) <= (ir_flag_t)(-1)) ? 1 : -1];
/* This code assumes 32 bit floats while generating binary */
extern int check_int_and_float_size
[ (sizeof(int32_t) == sizeof(qcfloat)) ? 1 : -1 ];
#endif

File diff suppressed because it is too large Load diff

343
lexer.h
View file

@ -1,19 +1,60 @@
/*
* Copyright (C) 2012
* Wolfgang Bumiller
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef GMQCC_LEXER_HDR
#define GMQCC_LEXER_HDR
#include "gmqcc.h"
struct token {
typedef struct token_s token;
#include "ast.h"
struct token_s {
int ttype;
char *value;
union {
vec3_t v;
int i;
qcfloat_t f;
qc_type t; /* type */
vector v;
int i;
double f;
int t; /* type */
} constval;
lex_ctx_t ctx;
#if 0
struct token_s *next;
struct token_s *prev;
#endif
lex_ctx ctx;
};
#if 0
token* token_new();
void token_delete(token*);
token* token_copy(const token *cp);
void token_delete_all(token *t);
token* token_copy_all(const token *cp);
#endif
/* Lexer
*
*/
@ -35,10 +76,6 @@ enum {
TOKEN_ATTRIBUTE_OPEN, /* [[ */
TOKEN_ATTRIBUTE_CLOSE, /* ]] */
TOKEN_VA_ARGS, /* for the ftepp only */
TOKEN_VA_ARGS_ARRAY, /* for the ftepp only */
TOKEN_VA_COUNT, /* to get the count of vaargs */
TOKEN_STRINGCONST, /* not the typename but an actual "string" */
TOKEN_CHARCONST,
TOKEN_VECTORCONST,
@ -48,11 +85,7 @@ enum {
TOKEN_WHITE,
TOKEN_EOL,
/* if we add additional tokens before this, the exposed API
* should not be broken anyway, but EOF/ERROR/... should
* still be at the bottom
*/
TOKEN_EOF = 1024,
TOKEN_EOF,
/* We use '< TOKEN_ERROR', so TOKEN_FATAL must come after it and any
* other error related tokens as well
@ -61,13 +94,13 @@ enum {
TOKEN_FATAL /* internal error, eg out of memory */
};
struct frame_macro {
typedef struct {
char *name;
int value;
};
int value;
} frame_macro;
struct lex_file {
FILE *file;
typedef struct {
FILE *file;
const char *open_string;
size_t open_string_length;
size_t open_string_pos;
@ -75,9 +108,8 @@ struct lex_file {
char *name;
size_t line;
size_t sline; /* line at the start of a token */
size_t column;
int peek[256];
char peek[256];
size_t peekpos;
bool eof;
@ -85,18 +117,18 @@ struct lex_file {
token tok; /* not a pointer anymore */
struct {
unsigned noops:1;
unsigned nodigraphs:1; /* used when lexing string constants */
unsigned preprocessing:1; /* whitespace and EOLs become actual tokens */
unsigned mergelines:1; /* backslash at the end of a line escapes the newline */
} flags; /* sizeof == 1 */
bool noops;
bool nodigraphs; /* used when lexing string constants */
bool preprocessing; /* whitespace and EOLs become actual tokens */
bool mergelines; /* backslash at the end of a line escapes the newline */
} flags;
int framevalue;
frame_macro *frames;
char *modelname;
size_t push_line;
};
} lex_file;
lex_file* lex_open (const char *file);
lex_file* lex_open_string(const char *str, size_t len, const char *name);
@ -116,188 +148,179 @@ enum {
#define OP_SUFFIX 1
#define OP_PREFIX 2
struct oper_info {
typedef struct {
const char *op;
unsigned int operands;
unsigned int id;
unsigned int assoc;
signed int prec;
unsigned int flags;
bool folds;
};
} oper_info;
/*
* Explicit uint8_t casts since the left operand of shift operator cannot
* be negative, even though it won't happen, this supresses the future
* possibility.
*/
#define opid1(a) ((uint8_t)a)
#define opid2(a,b) (((uint8_t)a<<8) |(uint8_t)b)
#define opid3(a,b,c) (((uint8_t)a<<16)|((uint8_t)b<<8)|(uint8_t)c)
#define opid1(a) (a)
#define opid2(a,b) ((a<<8)|b)
#define opid3(a,b,c) ((a<<16)|(b<<8)|c)
static const oper_info c_operators[] = {
{ "(", 0, opid1('('), ASSOC_LEFT, 99, OP_PREFIX, false}, /* paren expression - non function call */
{ "_length", 1, opid3('l','e','n'), ASSOC_RIGHT, 98, OP_PREFIX, true},
{ "(", 0, opid1('('), ASSOC_LEFT, 99, OP_PREFIX}, /* paren expression - non function call */
{ "++", 1, opid3('S','+','+'), ASSOC_LEFT, 17, OP_SUFFIX, false},
{ "--", 1, opid3('S','-','-'), ASSOC_LEFT, 17, OP_SUFFIX, false},
{ ".", 2, opid1('.'), ASSOC_LEFT, 17, 0, false},
{ "(", 0, opid1('('), ASSOC_LEFT, 17, 0, false}, /* function call */
{ "[", 2, opid1('['), ASSOC_LEFT, 17, 0, false}, /* array subscript */
{ "++", 1, opid3('S','+','+'), ASSOC_LEFT, 15, OP_SUFFIX},
{ "--", 1, opid3('S','-','-'), ASSOC_LEFT, 15, OP_SUFFIX},
{ ".", 2, opid1('.'), ASSOC_LEFT, 15, 0 },
{ "(", 0, opid1('('), ASSOC_LEFT, 15, 0 }, /* function call */
{ "[", 2, opid1('['), ASSOC_LEFT, 15, 0 }, /* array subscript */
{ "++", 1, opid3('+','+','P'), ASSOC_RIGHT, 16, OP_PREFIX, false},
{ "--", 1, opid3('-','-','P'), ASSOC_RIGHT, 16, OP_PREFIX, false},
{ "!", 1, opid2('!', 'P'), ASSOC_RIGHT, 14, OP_PREFIX },
{ "~", 1, opid2('~', 'P'), ASSOC_RIGHT, 14, OP_PREFIX },
{ "+", 1, opid2('+','P'), ASSOC_RIGHT, 14, OP_PREFIX },
{ "-", 1, opid2('-','P'), ASSOC_RIGHT, 14, OP_PREFIX },
{ "++", 1, opid3('+','+','P'), ASSOC_RIGHT, 14, OP_PREFIX },
{ "--", 1, opid3('-','-','P'), ASSOC_RIGHT, 14, OP_PREFIX },
/* { "&", 1, opid2('&','P'), ASSOC_RIGHT, 14, OP_PREFIX }, */
{ "**", 2, opid2('*','*'), ASSOC_RIGHT, 14, 0, true},
{ "!", 1, opid2('!','P'), ASSOC_RIGHT, 14, OP_PREFIX, true},
{ "~", 1, opid2('~','P'), ASSOC_RIGHT, 14, OP_PREFIX, true},
{ "+", 1, opid2('+','P'), ASSOC_RIGHT, 14, OP_PREFIX, false},
{ "-", 1, opid2('-','P'), ASSOC_RIGHT, 14, OP_PREFIX, true},
/* { "&", 1, opid2('&','P'), ASSOC_RIGHT, 14, OP_PREFIX, false}, */
{ "*", 2, opid1('*'), ASSOC_LEFT, 13, 0 },
{ "/", 2, opid1('/'), ASSOC_LEFT, 13, 0 },
{ "%", 2, opid1('%'), ASSOC_LEFT, 13, 0 },
{ "*", 2, opid1('*'), ASSOC_LEFT, 13, 0, true},
{ "/", 2, opid1('/'), ASSOC_LEFT, 13, 0, true},
{ "%", 2, opid1('%'), ASSOC_LEFT, 13, 0, true},
{ "><", 2, opid2('>','<'), ASSOC_LEFT, 13, 0, true},
{ "+", 2, opid1('+'), ASSOC_LEFT, 12, 0 },
{ "-", 2, opid1('-'), ASSOC_LEFT, 12, 0 },
{ "+", 2, opid1('+'), ASSOC_LEFT, 12, 0, true},
{ "-", 2, opid1('-'), ASSOC_LEFT, 12, 0, true},
{ "<<", 2, opid2('<','<'), ASSOC_LEFT, 11, 0 },
{ ">>", 2, opid2('>','>'), ASSOC_LEFT, 11, 0 },
{ "<<", 2, opid2('<','<'), ASSOC_LEFT, 11, 0, true},
{ ">>", 2, opid2('>','>'), ASSOC_LEFT, 11, 0, true},
{ "<", 2, opid1('<'), ASSOC_LEFT, 10, 0 },
{ ">", 2, opid1('>'), ASSOC_LEFT, 10, 0 },
{ "<=", 2, opid2('<','='), ASSOC_LEFT, 10, 0 },
{ ">=", 2, opid2('>','='), ASSOC_LEFT, 10, 0 },
{ "<", 2, opid1('<'), ASSOC_LEFT, 10, 0, false},
{ ">", 2, opid1('>'), ASSOC_LEFT, 10, 0, false},
{ "<=>", 2, opid3('<','=','>'), ASSOC_LEFT, 10, 0, true},
{ "<=", 2, opid2('<','='), ASSOC_LEFT, 10, 0, false},
{ ">=", 2, opid2('>','='), ASSOC_LEFT, 10, 0, false},
{ "==", 2, opid2('=','='), ASSOC_LEFT, 9, 0 },
{ "!=", 2, opid2('!','='), ASSOC_LEFT, 9, 0 },
{ "==", 2, opid2('=','='), ASSOC_LEFT, 9, 0, true},
{ "!=", 2, opid2('!','='), ASSOC_LEFT, 9, 0, true},
{ "&", 2, opid1('&'), ASSOC_LEFT, 8, 0 },
{ "&", 2, opid1('&'), ASSOC_LEFT, 8, 0, true},
{ "^", 2, opid1('^'), ASSOC_LEFT, 7, 0 },
{ "^", 2, opid1('^'), ASSOC_LEFT, 7, 0, true},
{ "|", 2, opid1('|'), ASSOC_LEFT, 6, 0 },
{ "|", 2, opid1('|'), ASSOC_LEFT, 6, 0, true},
{ "&&", 2, opid2('&','&'), ASSOC_LEFT, 5, 0 },
{ "&&", 2, opid2('&','&'), ASSOC_LEFT, 5, 0, true},
{ "||", 2, opid2('|','|'), ASSOC_LEFT, 4, 0 },
{ "||", 2, opid2('|','|'), ASSOC_LEFT, 4, 0, true},
{ "?", 3, opid2('?',':'), ASSOC_RIGHT, 3, 0 },
{ "?", 3, opid2('?',':'), ASSOC_RIGHT, 3, 0, true},
{ "=", 2, opid1('='), ASSOC_RIGHT, 2, 0 },
{ "+=", 2, opid2('+','='), ASSOC_RIGHT, 2, 0 },
{ "-=", 2, opid2('-','='), ASSOC_RIGHT, 2, 0 },
{ "*=", 2, opid2('*','='), ASSOC_RIGHT, 2, 0 },
{ "/=", 2, opid2('/','='), ASSOC_RIGHT, 2, 0 },
{ "%=", 2, opid2('%','='), ASSOC_RIGHT, 2, 0 },
{ ">>=", 2, opid3('>','>','='), ASSOC_RIGHT, 2, 0 },
{ "<<=", 2, opid3('<','<','='), ASSOC_RIGHT, 2, 0 },
{ "&=", 2, opid2('&','='), ASSOC_RIGHT, 2, 0 },
{ "^=", 2, opid2('^','='), ASSOC_RIGHT, 2, 0 },
{ "|=", 2, opid2('|','='), ASSOC_RIGHT, 2, 0 },
{ "=", 2, opid1('='), ASSOC_RIGHT, 2, 0, false},
{ "+=", 2, opid2('+','='), ASSOC_RIGHT, 2, 0, false},
{ "-=", 2, opid2('-','='), ASSOC_RIGHT, 2, 0, false},
{ "*=", 2, opid2('*','='), ASSOC_RIGHT, 2, 0, false},
{ "/=", 2, opid2('/','='), ASSOC_RIGHT, 2, 0, false},
{ "%=", 2, opid2('%','='), ASSOC_RIGHT, 2, 0, false},
{ ">>=", 2, opid3('>','>','='), ASSOC_RIGHT, 2, 0, false},
{ "<<=", 2, opid3('<','<','='), ASSOC_RIGHT, 2, 0, false},
{ "&=", 2, opid2('&','='), ASSOC_RIGHT, 2, 0, false},
{ "^=", 2, opid2('^','='), ASSOC_RIGHT, 2, 0, false},
{ "|=", 2, opid2('|','='), ASSOC_RIGHT, 2, 0, false},
{ ":", 0, opid2(':','?'), ASSOC_RIGHT, 1, 0 },
{ ":", 0, opid2(':','?'), ASSOC_RIGHT, 1, 0, false},
{ ",", 2, opid1(','), ASSOC_LEFT, 0, 0, false}
{ ",", 2, opid1(','), ASSOC_LEFT, 0, 0 }
};
static const size_t c_operator_count = (sizeof(c_operators) / sizeof(c_operators[0]));
static const oper_info fte_operators[] = {
{ "(", 0, opid1('('), ASSOC_LEFT, 99, OP_PREFIX, false}, /* paren expression - non function call */
{ "(", 0, opid1('('), ASSOC_LEFT, 99, OP_PREFIX}, /* paren expression - non function call */
{ "++", 1, opid3('S','+','+'), ASSOC_LEFT, 15, OP_SUFFIX, false},
{ "--", 1, opid3('S','-','-'), ASSOC_LEFT, 15, OP_SUFFIX, false},
{ ".", 2, opid1('.'), ASSOC_LEFT, 15, 0, false},
{ "(", 0, opid1('('), ASSOC_LEFT, 15, 0, false}, /* function call */
{ "[", 2, opid1('['), ASSOC_LEFT, 15, 0, false}, /* array subscript */
{ "++", 1, opid3('S','+','+'), ASSOC_LEFT, 15, OP_SUFFIX},
{ "--", 1, opid3('S','-','-'), ASSOC_LEFT, 15, OP_SUFFIX},
{ ".", 2, opid1('.'), ASSOC_LEFT, 15, 0 },
{ "(", 0, opid1('('), ASSOC_LEFT, 15, 0 }, /* function call */
{ "[", 2, opid1('['), ASSOC_LEFT, 15, 0 }, /* array subscript */
{ "!", 1, opid2('!','P'), ASSOC_RIGHT, 14, OP_PREFIX, true},
{ "+", 1, opid2('+','P'), ASSOC_RIGHT, 14, OP_PREFIX, false},
{ "-", 1, opid2('-','P'), ASSOC_RIGHT, 14, OP_PREFIX, true},
{ "++", 1, opid3('+','+','P'), ASSOC_RIGHT, 14, OP_PREFIX, false},
{ "--", 1, opid3('-','-','P'), ASSOC_RIGHT, 14, OP_PREFIX, false},
{ "!", 1, opid2('!', 'P'), ASSOC_RIGHT, 14, OP_PREFIX },
{ "+", 1, opid2('+','P'), ASSOC_RIGHT, 14, OP_PREFIX },
{ "-", 1, opid2('-','P'), ASSOC_RIGHT, 14, OP_PREFIX },
{ "++", 1, opid3('+','+','P'), ASSOC_RIGHT, 14, OP_PREFIX },
{ "--", 1, opid3('-','-','P'), ASSOC_RIGHT, 14, OP_PREFIX },
{ "*", 2, opid1('*'), ASSOC_LEFT, 13, 0, true},
{ "/", 2, opid1('/'), ASSOC_LEFT, 13, 0, true},
{ "&", 2, opid1('&'), ASSOC_LEFT, 13, 0, true},
{ "|", 2, opid1('|'), ASSOC_LEFT, 13, 0, true},
{ "*", 2, opid1('*'), ASSOC_LEFT, 13, 0 },
{ "/", 2, opid1('/'), ASSOC_LEFT, 13, 0 },
{ "&", 2, opid1('&'), ASSOC_LEFT, 13, 0 },
{ "|", 2, opid1('|'), ASSOC_LEFT, 13, 0 },
{ "+", 2, opid1('+'), ASSOC_LEFT, 12, 0, true},
{ "-", 2, opid1('-'), ASSOC_LEFT, 12, 0, true},
{ "+", 2, opid1('+'), ASSOC_LEFT, 12, 0 },
{ "-", 2, opid1('-'), ASSOC_LEFT, 12, 0 },
{ "<<", 2, opid2('<','<'), ASSOC_LEFT, 11, 0, true},
{ ">>", 2, opid2('>','>'), ASSOC_LEFT, 11, 0, true},
{ "<", 2, opid1('<'), ASSOC_LEFT, 10, 0 },
{ ">", 2, opid1('>'), ASSOC_LEFT, 10, 0 },
{ "<=", 2, opid2('<','='), ASSOC_LEFT, 10, 0 },
{ ">=", 2, opid2('>','='), ASSOC_LEFT, 10, 0 },
{ "==", 2, opid2('=','='), ASSOC_LEFT, 10, 0 },
{ "!=", 2, opid2('!','='), ASSOC_LEFT, 10, 0 },
{ "<", 2, opid1('<'), ASSOC_LEFT, 10, 0, false},
{ ">", 2, opid1('>'), ASSOC_LEFT, 10, 0, false},
{ "<=", 2, opid2('<','='), ASSOC_LEFT, 10, 0, false},
{ ">=", 2, opid2('>','='), ASSOC_LEFT, 10, 0, false},
{ "==", 2, opid2('=','='), ASSOC_LEFT, 10, 0, true},
{ "!=", 2, opid2('!','='), ASSOC_LEFT, 10, 0, true},
{ "?", 3, opid2('?',':'), ASSOC_RIGHT, 9, 0 },
{ "?", 3, opid2('?',':'), ASSOC_RIGHT, 9, 0, true},
{ "=", 2, opid1('='), ASSOC_RIGHT, 8, 0 },
{ "+=", 2, opid2('+','='), ASSOC_RIGHT, 8, 0 },
{ "-=", 2, opid2('-','='), ASSOC_RIGHT, 8, 0 },
{ "*=", 2, opid2('*','='), ASSOC_RIGHT, 8, 0 },
{ "/=", 2, opid2('/','='), ASSOC_RIGHT, 8, 0 },
{ "%=", 2, opid2('%','='), ASSOC_RIGHT, 8, 0 },
{ "&=", 2, opid2('&','='), ASSOC_RIGHT, 8, 0 },
{ "|=", 2, opid2('|','='), ASSOC_RIGHT, 8, 0 },
{ "&~=", 2, opid3('&','~','='), ASSOC_RIGHT, 8, 0 },
{ "=", 2, opid1('='), ASSOC_RIGHT, 8, 0, false},
{ "+=", 2, opid2('+','='), ASSOC_RIGHT, 8, 0, false},
{ "-=", 2, opid2('-','='), ASSOC_RIGHT, 8, 0, false},
{ "*=", 2, opid2('*','='), ASSOC_RIGHT, 8, 0, false},
{ "/=", 2, opid2('/','='), ASSOC_RIGHT, 8, 0, false},
{ "%=", 2, opid2('%','='), ASSOC_RIGHT, 8, 0, false},
{ "&=", 2, opid2('&','='), ASSOC_RIGHT, 8, 0, false},
{ "|=", 2, opid2('|','='), ASSOC_RIGHT, 8, 0, false},
{ "&~=", 2, opid3('&','~','='), ASSOC_RIGHT, 8, 0, false},
{ "&&", 2, opid2('&','&'), ASSOC_LEFT, 5, 0, true},
{ "||", 2, opid2('|','|'), ASSOC_LEFT, 5, 0, true},
{ "&&", 2, opid2('&','&'), ASSOC_LEFT, 5, 0 },
{ "||", 2, opid2('|','|'), ASSOC_LEFT, 5, 0 },
/* Leave precedence 3 for : with -fcorrect-ternary */
{ ",", 2, opid1(','), ASSOC_LEFT, 2, 0, false},
{ ":", 0, opid2(':','?'), ASSOC_RIGHT, 1, 0, false}
{ ",", 2, opid1(','), ASSOC_LEFT, 2, 0 },
{ ":", 0, opid2(':','?'), ASSOC_RIGHT, 1, 0 }
};
static const size_t fte_operator_count = (sizeof(fte_operators) / sizeof(fte_operators[0]));
static const oper_info qcc_operators[] = {
{ "(", 0, opid1('('), ASSOC_LEFT, 99, OP_PREFIX, false}, /* paren expression - non function call */
{ "(", 0, opid1('('), ASSOC_LEFT, 99, OP_PREFIX}, /* paren expression - non function call */
{ ".", 2, opid1('.'), ASSOC_LEFT, 15, 0, false},
{ "(", 0, opid1('('), ASSOC_LEFT, 15, 0, false}, /* function call */
{ "[", 2, opid1('['), ASSOC_LEFT, 15, 0, false}, /* array subscript */
{ ".", 2, opid1('.'), ASSOC_LEFT, 15, 0 },
{ "(", 0, opid1('('), ASSOC_LEFT, 15, 0 }, /* function call */
{ "[", 2, opid1('['), ASSOC_LEFT, 15, 0 }, /* array subscript */
{ "!", 1, opid2('!','P'), ASSOC_RIGHT, 14, OP_PREFIX, true},
{ "+", 1, opid2('+','P'), ASSOC_RIGHT, 14, OP_PREFIX, false},
{ "-", 1, opid2('-','P'), ASSOC_RIGHT, 14, OP_PREFIX, true},
{ "!", 1, opid2('!', 'P'), ASSOC_RIGHT, 14, OP_PREFIX },
{ "+", 1, opid2('+','P'), ASSOC_RIGHT, 14, OP_PREFIX },
{ "-", 1, opid2('-','P'), ASSOC_RIGHT, 14, OP_PREFIX },
{ "*", 2, opid1('*'), ASSOC_LEFT, 13, 0, true},
{ "/", 2, opid1('/'), ASSOC_LEFT, 13, 0, true},
{ "&", 2, opid1('&'), ASSOC_LEFT, 13, 0, true},
{ "|", 2, opid1('|'), ASSOC_LEFT, 13, 0, true},
{ "*", 2, opid1('*'), ASSOC_LEFT, 13, 0 },
{ "/", 2, opid1('/'), ASSOC_LEFT, 13, 0 },
{ "&", 2, opid1('&'), ASSOC_LEFT, 13, 0 },
{ "|", 2, opid1('|'), ASSOC_LEFT, 13, 0 },
{ "+", 2, opid1('+'), ASSOC_LEFT, 12, 0, true},
{ "-", 2, opid1('-'), ASSOC_LEFT, 12, 0, true},
{ "+", 2, opid1('+'), ASSOC_LEFT, 12, 0 },
{ "-", 2, opid1('-'), ASSOC_LEFT, 12, 0 },
{ "<", 2, opid1('<'), ASSOC_LEFT, 10, 0, false},
{ ">", 2, opid1('>'), ASSOC_LEFT, 10, 0, false},
{ "<=", 2, opid2('<','='), ASSOC_LEFT, 10, 0, false},
{ ">=", 2, opid2('>','='), ASSOC_LEFT, 10, 0, false},
{ "==", 2, opid2('=','='), ASSOC_LEFT, 10, 0, true},
{ "!=", 2, opid2('!','='), ASSOC_LEFT, 10, 0, true},
{ "<", 2, opid1('<'), ASSOC_LEFT, 10, 0 },
{ ">", 2, opid1('>'), ASSOC_LEFT, 10, 0 },
{ "<=", 2, opid2('<','='), ASSOC_LEFT, 10, 0 },
{ ">=", 2, opid2('>','='), ASSOC_LEFT, 10, 0 },
{ "==", 2, opid2('=','='), ASSOC_LEFT, 10, 0 },
{ "!=", 2, opid2('!','='), ASSOC_LEFT, 10, 0 },
{ "=", 2, opid1('='), ASSOC_RIGHT, 8, 0, false},
{ "+=", 2, opid2('+','='), ASSOC_RIGHT, 8, 0, false},
{ "-=", 2, opid2('-','='), ASSOC_RIGHT, 8, 0, false},
{ "*=", 2, opid2('*','='), ASSOC_RIGHT, 8, 0, false},
{ "/=", 2, opid2('/','='), ASSOC_RIGHT, 8, 0, false},
{ "%=", 2, opid2('%','='), ASSOC_RIGHT, 8, 0, false},
{ "&=", 2, opid2('&','='), ASSOC_RIGHT, 8, 0, false},
{ "|=", 2, opid2('|','='), ASSOC_RIGHT, 8, 0, false},
{ "=", 2, opid1('='), ASSOC_RIGHT, 8, 0 },
{ "+=", 2, opid2('+','='), ASSOC_RIGHT, 8, 0 },
{ "-=", 2, opid2('-','='), ASSOC_RIGHT, 8, 0 },
{ "*=", 2, opid2('*','='), ASSOC_RIGHT, 8, 0 },
{ "/=", 2, opid2('/','='), ASSOC_RIGHT, 8, 0 },
{ "%=", 2, opid2('%','='), ASSOC_RIGHT, 8, 0 },
{ "&=", 2, opid2('&','='), ASSOC_RIGHT, 8, 0 },
{ "|=", 2, opid2('|','='), ASSOC_RIGHT, 8, 0 },
{ "&&", 2, opid2('&','&'), ASSOC_LEFT, 5, 0, true},
{ "||", 2, opid2('|','|'), ASSOC_LEFT, 5, 0, true},
{ "&&", 2, opid2('&','&'), ASSOC_LEFT, 5, 0 },
{ "||", 2, opid2('|','|'), ASSOC_LEFT, 5, 0 },
{ ",", 2, opid1(','), ASSOC_LEFT, 2, 0, false},
{ ",", 2, opid1(','), ASSOC_LEFT, 2, 0 },
};
static const size_t qcc_operator_count = (sizeof(qcc_operators) / sizeof(qcc_operators[0]));
extern const oper_info *operators;
extern size_t operator_count;
void lexerror(lex_file*, const char *fmt, ...);
#endif

View file

@ -1,20 +1,40 @@
#include <stdlib.h>
#include <string.h>
/*
* Copyright (C) 2012
* Dale Weiler
* Wolfgang Bumiller
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "gmqcc.h"
#include "lexer.h"
#include "parser.h"
/* TODO: cleanup this whole file .. it's a fuckign mess */
/* set by the standard */
const oper_info *operators = nullptr;
size_t operator_count = 0;
static bool opts_output_wasset = false;
struct argitem { char *filename; int type; };
struct ppitem { char *name; char *value; };
static argitem *items = nullptr;
static ppitem *ppems = nullptr;
const oper_info *operators = NULL;
size_t operator_count = 0;
static bool opts_output_wasset = false;
typedef struct { char *filename; int type; } argitem;
typedef struct { char *name; char *value; } ppitem;
static argitem *items = NULL;
static ppitem *ppems = NULL;
#define TYPE_QC 0
#define TYPE_ASM 1
@ -22,8 +42,8 @@ static ppitem *ppems = nullptr;
static const char *app_name;
static void version(void) {
con_out("GMQCC %d.%d.%d Built %s %s\n" GMQCC_DEV_VERSION_STRING,
static void version() {
con_out("GMQCC %d.%d.%d Built %s %s\n",
GMQCC_VERSION_MAJOR,
GMQCC_VERSION_MINOR,
GMQCC_VERSION_PATCH,
@ -32,15 +52,15 @@ static void version(void) {
);
}
static int usage(void) {
static int usage() {
con_out("usage: %s [options] [files...]", app_name);
con_out("options:\n"
" -h, --help show this help message\n"
" -debug turns on compiler debug messages\n");
" -debug turns on compiler debug messages\n"
" -memchk turns on compiler memory leak check\n");
con_out(" -o, --output=file output file, defaults to progs.dat\n"
" -s filename add a progs.src file to be used\n");
con_out(" -E stop after preprocessing\n");
con_out(" -q, --quiet be less verbose\n");
con_out(" -config file use the specified ini file\n");
con_out(" -std=standard select one of the following standards\n"
" -std=qcc original QuakeC\n"
@ -61,8 +81,6 @@ static int usage(void) {
" -Ono-<name> disable specific optimization\n"
" -Ohelp list optimizations\n");
con_out(" -force-crc=num force a specific checksum into the header\n");
con_out(" -state-fps=num emulate OP_STATE with the specified FPS\n");
con_out(" -coverage add coverage support\n");
return -1;
}
@ -117,16 +135,18 @@ static bool options_long_gcc(const char *optname, int *argc_, char ***argv_, cha
return options_long_witharg_all(optname, argc_, argv_, out, 1, false);
}
static bool options_parse(int argc, char **argv, bool *has_progs_src) {
static bool options_parse(int argc, char **argv) {
bool argend = false;
size_t itr;
char buffer[1024];
char *config = nullptr;
char buffer[1024];
char *redirout = NULL;
char *redirerr = NULL;
char *config = NULL;
while (!argend && argc > 1) {
char *argarg;
argitem item;
ppitem macro;
ppitem macro;
++argv;
--argc;
@ -136,31 +156,14 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
if (options_long_gcc("std", &argc, &argv, &argarg)) {
if (!strcmp(argarg, "gmqcc") || !strcmp(argarg, "default")) {
opts_set(opts.flags, ADJUST_VECTOR_FIELDS, true);
opts_set(opts.flags, CORRECT_LOGIC, true);
opts_set(opts.flags, SHORT_LOGIC, true);
opts_set(opts.flags, UNTYPED_NIL, true);
opts_set(opts.flags, VARIADIC_ARGS, true);
opts_set(opts.flags, FALSE_EMPTY_STRINGS, false);
opts_set(opts.flags, TRUE_EMPTY_STRINGS, true);
opts_set(opts.flags, LOOP_LABELS, true);
opts_set(opts.flags, TRANSLATABLE_STRINGS, true);
opts_set(opts.flags, INITIALIZED_NONCONSTANTS, true);
opts_set(opts.werror, WARN_INVALID_PARAMETER_COUNT, true);
opts_set(opts.werror, WARN_MISSING_RETURN_VALUES, true);
opts_set(opts.flags, EXPRESSIONS_FOR_BUILTINS, true);
opts_set(opts.warn, WARN_BREAKDEF, true);
OPTS_OPTION_U32(OPTION_STANDARD) = COMPILER_GMQCC;
opts_set(opts.flags, ADJUST_VECTOR_FIELDS, true);
opts.standard = COMPILER_GMQCC;
} else if (!strcmp(argarg, "qcc")) {
opts_set(opts.flags, ADJUST_VECTOR_FIELDS, false);
opts_set(opts.flags, ASSIGN_FUNCTION_TYPES, true);
OPTS_OPTION_U32(OPTION_STANDARD) = COMPILER_QCC;
opts.standard = COMPILER_QCC;
} else if (!strcmp(argarg, "fte") || !strcmp(argarg, "fteqcc")) {
@ -168,16 +171,14 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
opts_set(opts.flags, TRANSLATABLE_STRINGS, true);
opts_set(opts.flags, ADJUST_VECTOR_FIELDS, false);
opts_set(opts.flags, ASSIGN_FUNCTION_TYPES, true);
opts_set(opts.flags, CORRECT_TERNARY, false);
opts_set(opts.warn, WARN_TERNARY_PRECEDENCE, true);
opts_set(opts.warn, WARN_BREAKDEF, true);
OPTS_OPTION_U32(OPTION_STANDARD) = COMPILER_FTEQCC;
opts_set(opts.flags, CORRECT_TERNARY, false);
opts.standard = COMPILER_FTEQCC;
} else if (!strcmp(argarg, "qccx")) {
opts_set(opts.flags, ADJUST_VECTOR_FIELDS, false);
OPTS_OPTION_U32(OPTION_STANDARD) = COMPILER_QCCX;
opts.standard = COMPILER_QCCX;
} else {
con_out("Unknown standard: %s\n", argarg);
@ -186,25 +187,22 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
continue;
}
if (options_long_gcc("force-crc", &argc, &argv, &argarg)) {
OPTS_OPTION_BOOL(OPTION_FORCECRC) = true;
OPTS_OPTION_U16 (OPTION_FORCED_CRC) = strtol(argarg, nullptr, 0);
opts.forcecrc = true;
opts.forced_crc = strtol(argarg, NULL, 0);
continue;
}
if (options_long_gcc("state-fps", &argc, &argv, &argarg)) {
OPTS_OPTION_U32(OPTION_STATE_FPS) = strtol(argarg, nullptr, 0);
opts_set(opts.flags, EMULATE_STATE, true);
if (options_long_gcc("redirout", &argc, &argv, &redirout)) {
con_change(redirout, redirerr);
continue;
}
if (options_long_gcc("redirerr", &argc, &argv, &redirerr)) {
con_change(redirout, redirerr);
continue;
}
if (options_long_gcc("config", &argc, &argv, &argarg)) {
config = argarg;
continue;
}
if (options_long_gcc("progsrc", &argc, &argv, &argarg)) {
OPTS_OPTION_STR(OPTION_PROGSRC) = argarg;
*has_progs_src = true;
continue;
}
/* show defaults (like pathscale) */
if (!strcmp(argv[0]+1, "show-defaults")) {
@ -230,25 +228,25 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
}
if (!strcmp(argv[0]+1, "debug")) {
OPTS_OPTION_BOOL(OPTION_DEBUG) = true;
opts.debug = true;
continue;
}
if (!strcmp(argv[0]+1, "dump")) {
OPTS_OPTION_BOOL(OPTION_DUMP) = true;
opts.dump = true;
continue;
}
if (!strcmp(argv[0]+1, "dumpfin")) {
OPTS_OPTION_BOOL(OPTION_DUMPFIN) = true;
opts.dumpfin = true;
continue;
}
if (!strcmp(argv[0]+1, "memchk")) {
opts.memchk = true;
continue;
}
if (!strcmp(argv[0]+1, "nocolor")) {
con_color(0);
continue;
}
if (!strcmp(argv[0]+1, "coverage")) {
OPTS_OPTION_BOOL(OPTION_COVERAGE) = true;
continue;
}
switch (argv[0][1]) {
/* -h, show usage but exit with 0 */
@ -262,18 +260,13 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
exit(0);
case 'E':
OPTS_OPTION_BOOL(OPTION_PP_ONLY) = true;
opts_set(opts.flags, FTEPP_PREDEFS, true); /* predefs on for -E */
opts.pp_only = true;
break;
/* debug turns on -flno */
case 'g':
opts_setflag("LNO", true);
OPTS_OPTION_BOOL(OPTION_G) = true;
break;
case 'q':
OPTS_OPTION_BOOL(OPTION_QUIET) = true;
opts.g = true;
break;
case 'D':
@ -284,7 +277,7 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
if (!(argarg = strchr(argv[0] + 2, '='))) {
macro.name = util_strdup(argv[0]+2);
macro.value = nullptr;
macro.value = NULL;
} else {
*argarg='\0'; /* terminate for name */
macro.name = util_strdup(argv[0]+2);
@ -297,7 +290,7 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
case 'f':
util_strtocmd(argv[0]+2, argv[0]+2, strlen(argv[0]+2)+1);
if (!strcmp(argv[0]+2, "HELP") || *(argv[0]+2) == '?') {
con_out("Possible flags:\n\n");
con_out("Possible flags:\n");
for (itr = 0; itr < COUNT_FLAGS; ++itr) {
util_strtononcmd(opts_flag_list[itr].name, buffer, sizeof(buffer));
con_out(" -f%s\n", buffer);
@ -322,37 +315,31 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
for (itr = 0; itr < COUNT_WARNINGS; ++itr) {
util_strtononcmd(opts_warn_list[itr].name, buffer, sizeof(buffer));
con_out(" -W%s\n", buffer);
if (itr == WARN_DEBUG)
con_out(" Warnings included by -Wall:\n");
}
exit(0);
}
else if (!strcmp(argv[0]+2, "NO_ERROR") ||
!strcmp(argv[0]+2, "NO_ERROR_ALL"))
{
for (itr = 0; itr < GMQCC_ARRAY_COUNT(opts.werror); ++itr)
for (itr = 0; itr < sizeof(opts.werror)/sizeof(opts.werror[0]); ++itr)
opts.werror[itr] = 0;
break;
}
else if (!strcmp(argv[0]+2, "ERROR") ||
!strcmp(argv[0]+2, "ERROR_ALL"))
{
opts_backup_non_Werror_all();
for (itr = 0; itr < GMQCC_ARRAY_COUNT(opts.werror); ++itr)
for (itr = 0; itr < sizeof(opts.werror)/sizeof(opts.werror[0]); ++itr)
opts.werror[itr] = 0xFFFFFFFFL;
opts_restore_non_Werror_all();
break;
}
else if (!strcmp(argv[0]+2, "NONE")) {
for (itr = 0; itr < GMQCC_ARRAY_COUNT(opts.warn); ++itr)
for (itr = 0; itr < sizeof(opts.warn)/sizeof(opts.warn[0]); ++itr)
opts.warn[itr] = 0;
break;
}
else if (!strcmp(argv[0]+2, "ALL")) {
opts_backup_non_Wall();
for (itr = 0; itr < GMQCC_ARRAY_COUNT(opts.warn); ++itr)
for (itr = 0; itr < sizeof(opts.warn)/sizeof(opts.warn[0]); ++itr)
opts.warn[itr] = 0xFFFFFFFFL;
opts_restore_non_Wall();
break;
}
else if (!strncmp(argv[0]+2, "ERROR_", 6)) {
@ -384,10 +371,9 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
con_out("option -O requires a numerical argument, or optimization name with an optional 'no-' prefix\n");
return false;
}
if (util_isdigit(argarg[0])) {
uint32_t val = (uint32_t)strtol(argarg, nullptr, 10);
OPTS_OPTION_U32(OPTION_O) = val;
opts_setoptimlevel(val);
if (isdigit(argarg[0])) {
opts.O = atoi(argarg);
opts_setoptimlevel(opts.O);
} else {
util_strtocmd(argarg, argarg, strlen(argarg)+1);
if (!strcmp(argarg, "HELP")) {
@ -399,10 +385,9 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
exit(0);
}
else if (!strcmp(argarg, "ALL"))
opts_setoptimlevel(OPTS_OPTION_U32(OPTION_O) = 9999);
opts_setoptimlevel(opts.O = 9999);
else if (!strncmp(argarg, "NO_", 3)) {
/* constant folding cannot be turned off for obvious reasons */
if (!strcmp(argarg, "NO_CONST_FOLD") || !opts_setoptim(argarg+3, false)) {
if (!opts_setoptim(argarg+3, false)) {
con_out("unknown optimization: %s\n", argarg+3);
return false;
}
@ -421,7 +406,7 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
con_out("option -o requires an argument: the output file name\n");
return false;
}
OPTS_OPTION_STR(OPTION_OUTPUT) = argarg;
opts.output = argarg;
opts_output_wasset = true;
break;
@ -435,9 +420,6 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
}
item.filename = argarg;
vec_push(items, item);
if (item.type == TYPE_SRC) {
*has_progs_src = true;
}
break;
case '-':
@ -455,18 +437,10 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
version();
exit(0);
}
else if (!strcmp(argv[0]+2, "quiet")) {
OPTS_OPTION_BOOL(OPTION_QUIET) = true;
break;
}
else if (!strcmp(argv[0]+2, "add-info")) {
OPTS_OPTION_BOOL(OPTION_ADD_INFO) = true;
break;
}
else {
/* All long options with arguments */
if (options_long_witharg("output", &argc, &argv, &argarg)) {
OPTS_OPTION_STR(OPTION_OUTPUT) = argarg;
opts.output = argarg;
opts_output_wasset = true;
} else {
con_out("Unknown parameter: %s\n", argv[0]);
@ -493,21 +467,21 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
}
/* returns the line number, or -1 on error */
static bool progs_nextline(char **out, size_t *alen, FILE *src) {
static bool progs_nextline(char **out, size_t *alen,FILE *src) {
int len;
char *line;
char *start;
char *end;
line = *out;
len = util_getline(&line, alen, src);
len = file_getline(&line, alen, src);
if (len == -1)
return false;
/* start at first non-blank */
for (start = line; util_isspace(*start); ++start) {}
for (start = line; isspace(*start); ++start) {}
/* end at the first non-blank */
for (end = start; *end && !util_isspace(*end); ++end) {}
for (end = start; *end && !isspace(*end); ++end) {}
*out = line;
/* move the actual filename to the beginning */
@ -520,38 +494,35 @@ static bool progs_nextline(char **out, size_t *alen, FILE *src) {
int main(int argc, char **argv) {
size_t itr;
int retval = 0;
bool operators_free = false;
bool has_progs_src = false;
FILE *outfile = nullptr;
parser_t *parser = nullptr;
ftepp_t *ftepp = nullptr;
int retval = 0;
bool opts_output_free = false;
bool operators_free = false;
bool progs_src = false;
FILE *outfile = NULL;
app_name = argv[0];
con_init ();
opts_init("progs.dat", COMPILER_QCC, (1024 << 3));
opts_init("progs.dat", COMPILER_GMQCC, (1024 << 3));
util_seed(time(0));
if (!options_parse(argc, argv, &has_progs_src)) {
if (!options_parse(argc, argv)) {
return usage();
}
if (OPTS_FLAG(TRUE_EMPTY_STRINGS) && OPTS_FLAG(FALSE_EMPTY_STRINGS)) {
con_err("-ftrue-empty-strings and -ffalse-empty-strings are mutually exclusive");
exit(EXIT_FAILURE);
exit(1);
}
/* the standard decides which set of operators to use */
if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_GMQCC) {
if (opts.standard == COMPILER_GMQCC) {
operators = c_operators;
operator_count = GMQCC_ARRAY_COUNT(c_operators);
} else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC) {
operator_count = c_operator_count;
} else if (opts.standard == COMPILER_FTEQCC) {
operators = fte_operators;
operator_count = GMQCC_ARRAY_COUNT(fte_operators);
operator_count = fte_operator_count;
} else {
operators = qcc_operators;
operator_count = GMQCC_ARRAY_COUNT(qcc_operators);
operator_count = qcc_operator_count;
}
if (operators == fte_operators) {
@ -562,7 +533,7 @@ int main(int argc, char **argv) {
operators[operator_count-1].id != opid2(':','?'))
{
con_err("internal error: operator precedence table wasn't updated correctly!\n");
exit(EXIT_FAILURE);
exit(1);
}
operators_free = true;
newops = (oper_info*)mem_a(sizeof(operators[0]) * operator_count);
@ -574,22 +545,22 @@ int main(int argc, char **argv) {
}
}
if (OPTS_OPTION_BOOL(OPTION_DUMP)) {
if (opts.dump) {
for (itr = 0; itr < COUNT_FLAGS; ++itr)
con_out("Flag %s = %i\n", opts_flag_list[itr].name, OPTS_FLAG(itr));
for (itr = 0; itr < COUNT_WARNINGS; ++itr)
con_out("Warning %s = %i\n", opts_warn_list[itr].name, OPTS_WARN(itr));
con_out("output = %s\n", OPTS_OPTION_STR(OPTION_OUTPUT));
con_out("optimization level = %u\n", OPTS_OPTION_U32(OPTION_O));
con_out("standard = %u\n", OPTS_OPTION_U32(OPTION_STANDARD));
con_out("output = %s\n", opts.output);
con_out("optimization level = %d\n", opts.O);
con_out("standard = %i\n", opts.standard);
}
if (OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
if (opts.pp_only) {
if (opts_output_wasset) {
outfile = fopen(OPTS_OPTION_STR(OPTION_OUTPUT), "wb");
outfile = file_open(opts.output, "wb");
if (!outfile) {
con_err("failed to open `%s` for writing\n", OPTS_OPTION_STR(OPTION_OUTPUT));
con_err("failed to open `%s` for writing\n", opts.output);
retval = 1;
goto cleanup;
}
@ -599,26 +570,31 @@ int main(int argc, char **argv) {
}
}
if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
if (!(parser = parser_create())) {
if (!opts.pp_only) {
if (!parser_init()) {
con_err("failed to initialize parser\n");
retval = 1;
goto cleanup;
}
}
if (OPTS_OPTION_BOOL(OPTION_PP_ONLY) || OPTS_FLAG(FTEPP)) {
if (!(ftepp = ftepp_create())) {
if (opts.pp_only || OPTS_FLAG(FTEPP)) {
if (!ftepp_init()) {
con_err("failed to initialize parser\n");
retval = 1;
goto cleanup;
}
}
if (OPTS_FLAG(TRUE_EMPTY_STRINGS))
type_not_instr[TYPE_STRING] = INSTR_NOT_F;
util_debug("COM", "starting ...\n");
/* add macros */
if (OPTS_OPTION_BOOL(OPTION_PP_ONLY) || OPTS_FLAG(FTEPP)) {
if (opts.pp_only || OPTS_FLAG(FTEPP)) {
for (itr = 0; itr < vec_size(ppems); itr++) {
ftepp_add_macro(ftepp, ppems[itr].name, ppems[itr].value);
ftepp_add_macro(ppems[itr].name, ppems[itr].value);
mem_d(ppems[itr].name);
/* can be null */
@ -627,62 +603,56 @@ int main(int argc, char **argv) {
}
}
if (!vec_size(items) && !has_progs_src) {
FILE *fp = fopen(OPTS_OPTION_STR(OPTION_PROGSRC), "rb");
if (fp) {
has_progs_src = true;
fclose(fp);
}
}
if (has_progs_src) {
if (!vec_size(items)) {
FILE *src;
char *line = nullptr;
char *line;
size_t linelen = 0;
bool has_first_line = false;
src = fopen(OPTS_OPTION_STR(OPTION_PROGSRC), "rb");
progs_src = true;
src = file_open("progs.src", "rb");
if (!src) {
con_err("failed to open `%s` for reading\n", OPTS_OPTION_STR(OPTION_PROGSRC));
con_err("failed to open `progs.src` for reading\n");
retval = 1;
goto cleanup;
}
while (progs_nextline(&line, &linelen, src)) {
argitem item;
if (!line[0] || (line[0] == '/' && line[1] == '/')) {
continue;
}
if (has_first_line) {
item.filename = util_strdup(line);
item.type = TYPE_QC;
vec_push(items, item);
} else {
if (!opts_output_wasset) {
OPTS_OPTION_DUP(OPTION_OUTPUT) = util_strdup(line);
}
has_first_line = true;
}
line = NULL;
if (!progs_nextline(&line, &linelen, src) || !line[0]) {
con_err("illformatted progs.src file: expected output filename in first line\n");
retval = 1;
goto srcdone;
}
fclose(src);
if (!opts_output_wasset) {
opts.output = util_strdup(line);
opts_output_free = true;
}
while (progs_nextline(&line, &linelen, src)) {
argitem item;
if (!line[0] || (line[0] == '/' && line[1] == '/'))
continue;
item.filename = util_strdup(line);
item.type = TYPE_QC;
vec_push(items, item);
}
srcdone:
file_close(src);
mem_d(line);
}
if (retval)
goto cleanup;
if (vec_size(items)) {
if (!OPTS_OPTION_BOOL(OPTION_QUIET) &&
!OPTS_OPTION_BOOL(OPTION_PP_ONLY))
{
con_out("Mode: %s\n", (has_progs_src ? "progs.src" : "manual"));
if (!opts.pp_only) {
con_out("Mode: %s\n", (progs_src ? "progs.src" : "manual"));
con_out("There are %lu items to compile:\n", (unsigned long)vec_size(items));
}
for (itr = 0; itr < vec_size(items); ++itr) {
if (!OPTS_OPTION_BOOL(OPTION_QUIET) &&
!OPTS_OPTION_BOOL(OPTION_PP_ONLY))
{
if (!opts.pp_only) {
con_out(" item: %s (%s)\n",
items[itr].filename,
( (items[itr].type == TYPE_QC ? "qc" :
@ -691,82 +661,80 @@ int main(int argc, char **argv) {
("unknown"))))));
}
if (items[itr].type == TYPE_SRC) {
continue;
}
if (OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
if (opts.pp_only) {
const char *out;
if (!ftepp_preprocess_file(ftepp, items[itr].filename)) {
if (!ftepp_preprocess_file(items[itr].filename)) {
retval = 1;
goto cleanup;
}
out = ftepp_get(ftepp);
out = ftepp_get();
if (out)
fprintf(outfile, "%s", out);
ftepp_flush(ftepp);
file_printf(outfile, "%s", out);
ftepp_flush();
}
else {
if (OPTS_FLAG(FTEPP)) {
const char *data;
if (!ftepp_preprocess_file(ftepp, items[itr].filename)) {
if (!ftepp_preprocess_file(items[itr].filename)) {
retval = 1;
goto cleanup;
}
data = ftepp_get(ftepp);
data = ftepp_get();
if (vec_size(data)) {
if (!parser_compile_string(parser, items[itr].filename, data, vec_size(data))) {
if (!parser_compile_string(items[itr].filename, data, vec_size(data))) {
retval = 1;
goto cleanup;
}
}
ftepp_flush(ftepp);
ftepp_flush();
}
else {
if (!parser_compile_file(parser, items[itr].filename)) {
if (!parser_compile_file(items[itr].filename)) {
retval = 1;
goto cleanup;
}
}
}
if (has_progs_src) {
if (progs_src) {
mem_d(items[itr].filename);
items[itr].filename = nullptr;
items[itr].filename = NULL;
}
}
ftepp_finish(ftepp);
ftepp = nullptr;
if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
if (!parser_finish(parser, OPTS_OPTION_STR(OPTION_OUTPUT))) {
ftepp_finish();
if (!opts.pp_only) {
if (!parser_finish(opts.output)) {
retval = 1;
goto cleanup;
}
}
}
/* stuff */
if (!opts.pp_only) {
for (itr = 0; itr < COUNT_OPTIMIZATIONS; ++itr) {
if (opts_optimizationcount[itr]) {
con_out("%s: %u\n", opts_opt_list[itr].name, (unsigned int)opts_optimizationcount[itr]);
}
}
}
cleanup:
if (ftepp)
ftepp_finish(ftepp);
util_debug("COM", "cleaning ...\n");
ftepp_finish();
con_close();
vec_free(items);
vec_free(ppems);
if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY))
delete parser;
/* free allocated option strings */
for (itr = 0; itr < OPTION_COUNT; itr++)
if (OPTS_OPTION_DUPED(itr))
mem_d(OPTS_OPTION_STR(itr));
if (!opts.pp_only)
parser_cleanup();
if (opts_output_free)
mem_d((char*)opts.output);
if (operators_free)
mem_d((void*)operators);
lex_cleanup();
if (!retval && compile_errors)
retval = 1;
util_meminfo();
return retval;
}

View file

@ -1,51 +0,0 @@
#!/bin/sh
prog=$0
die() {
echo "$@"
exit 1
}
want() {
test -e "$1" && return
echo "$prog: missing $1"
echo "$prog: run this script from the top of a gmqcc source tree"
exit 1
}
for i in opts.def \
doc/gmqcc.1 \
gmqcc.ini.example
do want "$i"; done
# y/_ABCDEFGHIJKLMNOPQRSTUVWXYZ/-abcdefghijklmnopqrstuvwxyz/;
check_opt() {
opt_def_name=$1
arg_char=$2
for i in $(sed -ne \
'/^#ifdef GMQCC_TYPE_'${opt_def_name}'$/,/^#endif/{
/GMQCC_DEFINE_FLAG/{
s/^.*GMQCC_DEFINE_FLAG(\([^,)]*\)[),].*$/\1/;p;
}
}' opts.def)
do
opt=$(echo "$i" | tr -- '_A-Z' '-a-z')
grep -qF -- ".It Fl "${arg_char}" Ns Cm $opt" \
doc/gmqcc.1 || echo "doc/gmqcc.1: missing: -${arg_char}$opt"
grep -q -- "[^a-zA-Z_]$i[^a-zA-Z_]" \
gmqcc.ini.example || echo "gmqcc.ini.example: missing: $i"
done
}
check_opt FLAGS f
check_opt WARNS W
check_opt OPTIMIZATIONS O
# TODO: linux version
if [ "$(uname -s)" != "Linux" ]; then
for i in doc/*.1;
do
mandoc -Tlint -Wall "$i";
done
fi

25
msvc/gmqcc.sln Normal file
View file

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qcvm", "qcvm.vcxproj", "{8DC505A6-6047-4683-BA81-BC4B7A839352}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gmqcc", "gmqcc.vcxproj", "{0F0B0779-1A2F-43E9-B833-18C443F7229E}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testsuite", "testsuite.vcxproj", "{3F8F0021-66B8-43ED-906C-1CFE204E5673}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8DC505A6-6047-4683-BA81-BC4B7A839352}.Release|Win32.ActiveCfg = Release|Win32
{8DC505A6-6047-4683-BA81-BC4B7A839352}.Release|Win32.Build.0 = Release|Win32
{0F0B0779-1A2F-43E9-B833-18C443F7229E}.Release|Win32.ActiveCfg = Release|Win32
{0F0B0779-1A2F-43E9-B833-18C443F7229E}.Release|Win32.Build.0 = Release|Win32
{3F8F0021-66B8-43ED-906C-1CFE204E5673}.Release|Win32.ActiveCfg = Release|Win32
{3F8F0021-66B8-43ED-906C-1CFE204E5673}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

78
msvc/gmqcc.vcxproj Normal file
View file

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{0F0B0779-1A2F-43E9-B833-18C443F7229E}</ProjectGuid>
<RootNamespace>gmqcc</RootNamespace>
<TrackFileAccess>false</TrackFileAccess>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<EmbedManifest>false</EmbedManifest>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
</ClCompile>
<Link>
<GenerateDebugInformation>false</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<ProgramDatabaseFile>
</ProgramDatabaseFile>
</Link>
<PostBuildEvent>
<Command>Del /Q "$(IntDir)\*.tlog"</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\ast.c">
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)</ObjectFileName>
</ClCompile>
<ClCompile Include="..\code.c">
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)</ObjectFileName>
</ClCompile>
<ClCompile Include="..\conout.c">
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)conout_gmqcc.o</ObjectFileName>
</ClCompile>
<ClCompile Include="..\ftepp.c" />
<ClCompile Include="..\ir.c" />
<ClCompile Include="..\lexer.c" />
<ClCompile Include="..\main.c" />
<ClCompile Include="..\opts.c" />
<ClCompile Include="..\parser.c" />
<ClCompile Include="..\util.c">
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)util_gmqcc.o</ObjectFileName>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\ast.h" />
<ClInclude Include="..\gmqcc.h" />
<ClInclude Include="..\ir.h" />
<ClInclude Include="..\lexer.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

67
msvc/qcvm.vcxproj Normal file
View file

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{8DC505A6-6047-4683-BA81-BC4B7A839352}</ProjectGuid>
<RootNamespace>qcvm</RootNamespace>
<TrackFileAccess>false</TrackFileAccess>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Full</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<OmitFramePointers>true</OmitFramePointers>
<EnableFiberSafeOptimizations>true</EnableFiberSafeOptimizations>
<PreprocessorDefinitions>QCVM_EXECUTOR=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
<PostBuildEvent>
<Command>Del /Q "$(IntDir)\*.tlog"</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\conout.c">
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)conout_qcvm.o</ObjectFileName>
</ClCompile>
<ClCompile Include="..\exec.c">
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)</ObjectFileName>
</ClCompile>
<ClCompile Include="..\util.c">
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)util_qcvm.o</ObjectFileName>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\gmqcc.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

58
msvc/testsuite.vcxproj Normal file
View file

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{3F8F0021-66B8-43ED-906C-1CFE204E5673}</ProjectGuid>
<RootNamespace>testsuite</RootNamespace>
<TrackFileAccess>false</TrackFileAccess>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
<PostBuildEvent>
<Command>Del /Q "$(IntDir)\*.tlog"</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\conout.c">
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)conout_testsuite.o</ObjectFileName>
</ClCompile>
<ClCompile Include="..\test.c" />
<ClCompile Include="..\util.c">
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)util_testsuite.o</ObjectFileName>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\gmqcc.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -1,43 +1,33 @@
#include <string.h>
#include <stdlib.h>
/*
* Copyright (C) 2012
* Wolfgang Bumiller
* Dale Weiler
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "gmqcc.h"
const unsigned int opts_opt_oflag[COUNT_OPTIMIZATIONS+1] = {
# define GMQCC_TYPE_OPTIMIZATIONS
# define GMQCC_DEFINE_FLAG(NAME, MIN_O) MIN_O,
# include "opts.def"
0
};
const opts_flag_def_t opts_opt_list[COUNT_OPTIMIZATIONS+1] = {
# define GMQCC_TYPE_OPTIMIZATIONS
# define GMQCC_DEFINE_FLAG(NAME, MIN_O) { #NAME, LONGBIT(OPTIM_##NAME) },
# include "opts.def"
{ nullptr, LONGBIT(0) }
};
const opts_flag_def_t opts_warn_list[COUNT_WARNINGS+1] = {
# define GMQCC_TYPE_WARNS
# define GMQCC_DEFINE_FLAG(X) { #X, LONGBIT(WARN_##X) },
# include "opts.def"
{ nullptr, LONGBIT(0) }
};
const opts_flag_def_t opts_flag_list[COUNT_FLAGS+1] = {
# define GMQCC_TYPE_FLAGS
# define GMQCC_DEFINE_FLAG(X) { #X, LONGBIT(X) },
# include "opts.def"
{ nullptr, LONGBIT(0) }
};
unsigned int opts_optimizationcount[COUNT_OPTIMIZATIONS];
opts_cmd_t opts; /* command line options */
opts_cmd_t opts; /* command lien options */
static void opts_setdefault(void) {
static void opts_setdefault() {
memset(&opts, 0, sizeof(opts_cmd_t));
OPTS_OPTION_STR(OPTION_PROGSRC) = "progs.src";
/* warnings */
opts_set(opts.warn, WARN_UNUSED_VARIABLE, true);
opts_set(opts.warn, WARN_USED_UNINITIALIZED, true);
@ -45,7 +35,8 @@ static void opts_setdefault(void) {
opts_set(opts.warn, WARN_EXTENSIONS, true);
opts_set(opts.warn, WARN_FIELD_REDECLARED, true);
opts_set(opts.warn, WARN_MISSING_RETURN_VALUES, true);
opts_set(opts.warn, WARN_INVALID_PARAMETER_COUNT, true);
opts_set(opts.warn, WARN_TOO_FEW_PARAMETERS, true);
opts_set(opts.warn, WARN_LOCAL_SHADOWS, false);
opts_set(opts.warn, WARN_LOCAL_CONSTANTS, true);
opts_set(opts.warn, WARN_VOID_VARIABLES, true);
opts_set(opts.warn, WARN_IMPLICIT_FUNCTION_POINTER, true);
@ -54,78 +45,46 @@ static void opts_setdefault(void) {
opts_set(opts.warn, WARN_EFFECTLESS_STATEMENT, true);
opts_set(opts.warn, WARN_END_SYS_FIELDS, true);
opts_set(opts.warn, WARN_ASSIGN_FUNCTION_TYPES, true);
opts_set(opts.warn, WARN_CPP, true);
opts_set(opts.warn, WARN_PREPROCESSOR, true);
opts_set(opts.warn, WARN_MULTIFILE_IF, true);
opts_set(opts.warn, WARN_DOUBLE_DECLARATION, true);
opts_set(opts.warn, WARN_CONST_VAR, true);
opts_set(opts.warn, WARN_MULTIBYTE_CHARACTER, true);
opts_set(opts.warn, WARN_UNKNOWN_PRAGMAS, true);
opts_set(opts.warn, WARN_UNREACHABLE_CODE, true);
opts_set(opts.warn, WARN_CPP, true);
opts_set(opts.warn, WARN_UNKNOWN_ATTRIBUTE, true);
opts_set(opts.warn, WARN_RESERVED_NAMES, true);
opts_set(opts.warn, WARN_UNINITIALIZED_CONSTANT, true);
opts_set(opts.warn, WARN_DEPRECATED, true);
opts_set(opts.warn, WARN_PARENTHESIS, true);
opts_set(opts.warn, WARN_CONST_OVERWRITE, true);
opts_set(opts.warn, WARN_DIRECTIVE_INMACRO, true);
opts_set(opts.warn, WARN_BUILTINS, true);
opts_set(opts.warn, WARN_INEXACT_COMPARES, true);
/* flags */
opts_set(opts.flags, ADJUST_VECTOR_FIELDS, true);
opts_set(opts.flags, FTEPP, false);
opts_set(opts.flags, CORRECT_TERNARY, true);
opts_set(opts.flags, BAIL_ON_WERROR, true);
opts_set(opts.flags, LEGACY_VECTOR_MATHS, true);
opts_set(opts.flags, DARKPLACES_STRING_TABLE_BUG, true);
/* options */
OPTS_OPTION_U32(OPTION_STATE_FPS) = 10;
}
void opts_backup_non_Wall() {
size_t i;
for (i = 0; i <= WARN_DEBUG; ++i)
opts_set(opts.warn_backup, i, OPTS_WARN(i));
}
void opts_restore_non_Wall() {
size_t i;
for (i = 0; i <= WARN_DEBUG; ++i)
opts_set(opts.warn, i, OPTS_GENERIC(opts.warn_backup, i));
}
void opts_backup_non_Werror_all() {
size_t i;
for (i = 0; i <= WARN_DEBUG; ++i)
opts_set(opts.werror_backup, i, OPTS_WERROR(i));
}
void opts_restore_non_Werror_all() {
size_t i;
for (i = 0; i <= WARN_DEBUG; ++i)
opts_set(opts.werror, i, OPTS_GENERIC(opts.werror_backup, i));
}
void opts_init(const char *output, int standard, size_t arraysize) {
opts_setdefault();
OPTS_OPTION_STR(OPTION_OUTPUT) = output;
OPTS_OPTION_U32(OPTION_STANDARD) = standard;
OPTS_OPTION_U32(OPTION_MAX_ARRAY_SIZE) = arraysize;
opts.output = output;
opts.standard = (opts_std_t)standard; /* C++ ... y u no like me? */
opts.max_array_size = arraysize;
}
static bool opts_setflag_all(const char *name, bool on, uint32_t *flags, const opts_flag_def_t *list, size_t listsize) {
static bool opts_setflag_all(const char *name, bool on, uint32_t *flags, const opts_flag_def *list, size_t listsize) {
size_t i;
for (i = 0; i < listsize; ++i) {
if (!strcmp(name, list[i].name)) {
longbit lb = list[i].bit;
#if 0
if (on)
flags[lb.idx] |= (1<<(lb.bit));
else
flags[lb.idx] &= ~(1<<(lb.bit));
#else
if (on)
flags[0] |= (1<<lb);
else
flags[0] &= ~(1<<(lb));
#endif
return true;
}
}
@ -145,22 +104,24 @@ bool opts_setoptim (const char *name, bool on) {
}
void opts_set(uint32_t *flags, size_t idx, bool on) {
longbit lb;
LONGBIT_SET(lb, idx);
longbit lb = LONGBIT(idx);
#if 0
if (on)
flags[lb.idx] |= (1u<<(lb.bit));
flags[lb.idx] |= (1<<(lb.bit));
else
flags[lb.idx] &= ~(1u<<(lb.bit));
flags[lb.idx] &= ~(1<<(lb.bit));
#else
if (on)
flags[0] |= (1<<(lb));
else
flags[0] &= ~(1<<(lb));
#endif
}
void opts_setoptimlevel(unsigned int level) {
size_t i;
for (i = 0; i < COUNT_OPTIMIZATIONS; ++i)
opts_set(opts.optimization, i, level >= opts_opt_oflag[i]);
if (!level)
opts.optimizeoff = true;
}
/*
@ -171,14 +132,14 @@ void opts_setoptimlevel(unsigned int level) {
* from a progs.src.
*/
static char *opts_ini_rstrip(char *s) {
char *p = s + strlen(s) - 1;
while (p > s && util_isspace(*p))
*p = '\0', p--;
char *p = s + strlen(s);
while(p > s && isspace(*--p))
*p = '\0';
return s;
}
static char *opts_ini_lskip(const char *s) {
while (*s && util_isspace(*s))
while (*s && isspace(*s))
s++;
return (char*)s;
}
@ -186,21 +147,20 @@ static char *opts_ini_lskip(const char *s) {
static char *opts_ini_next(const char *s, char c) {
bool last = false;
while (*s && *s != c && !(last && *s == ';'))
last = !!util_isspace(*s), s++;
last = !!isspace(*s), s++;
return (char*)s;
}
static size_t opts_ini_parse (
FILE *filehandle,
char *(*loadhandle)(const char *, const char *, const char *, char **),
char **errorhandle,
char **parse_file
FILE *filehandle,
char *(*loadhandle)(const char *, const char *, const char *),
char **errorhandle
) {
size_t linesize;
size_t lineno = 1;
size_t error = 0;
char *line = nullptr;
char *line = NULL;
char section_data[2048] = "";
char oldname_data[2048] = "";
@ -210,7 +170,7 @@ static size_t opts_ini_parse (
char *read_name;
char *read_value;
while (util_getline(&line, &linesize, filehandle) != EOF) {
while (file_getline(&line, &linesize, filehandle) != EOF) {
parse_beg = line;
/* handle BOM */
@ -231,7 +191,7 @@ static size_t opts_ini_parse (
/* section found */
if (*(parse_end = opts_ini_next(parse_beg + 1, ']')) == ']') {
* parse_end = '\0'; /* terminate bro */
util_strncpy(section_data, parse_beg + 1, sizeof(section_data));
strncpy(section_data, parse_beg + 1, sizeof(section_data));
section_data[sizeof(section_data) - 1] = '\0';
*oldname_data = '\0';
} else if (!error) {
@ -241,7 +201,7 @@ static size_t opts_ini_parse (
} else if (*parse_beg && *parse_beg != ';') {
/* not a comment, must be a name value pair :) */
if (*(parse_end = opts_ini_next(parse_beg, '=')) != '=')
parse_end = opts_ini_next(parse_beg, ':');
parse_end = opts_ini_next(parse_beg, ':');
if (*parse_end == '=' || *parse_end == ':') {
*parse_end = '\0'; /* terminate bro */
@ -252,23 +212,11 @@ static size_t opts_ini_parse (
opts_ini_rstrip(read_value);
/* valid name value pair, lets call down to handler */
util_strncpy(oldname_data, read_name, sizeof(oldname_data));
strncpy(oldname_data, read_name, sizeof(oldname_data));
oldname_data[sizeof(oldname_data) - 1] ='\0';
if ((*errorhandle = loadhandle(section_data, read_name, read_value, parse_file)) && !error)
if ((*errorhandle = loadhandle(section_data, read_name, read_value)) && !error)
error = lineno;
} else if (!strcmp(section_data, "includes")) {
/* Includes are special */
if (*(parse_end = opts_ini_next(parse_beg, '=')) == '='
|| *(parse_end = opts_ini_next(parse_beg, ':')) == ':') {
static const char *invalid_include = "invalid use of include";
vec_append(*errorhandle, strlen(invalid_include), invalid_include);
error = lineno;
} else {
read_name = opts_ini_rstrip(parse_beg);
if ((*errorhandle = loadhandle(section_data, read_name, read_name, parse_file)) && !error)
error = lineno;
}
} else if (!error) {
/* otherwise set error to the current line number */
error = lineno;
@ -278,7 +226,6 @@ static size_t opts_ini_parse (
}
mem_d(line);
return error;
}
/*
@ -287,41 +234,21 @@ static size_t opts_ini_parse (
static bool opts_ini_bool(const char *value) {
if (!strcmp(value, "true")) return true;
if (!strcmp(value, "false")) return false;
return !!strtol(value, nullptr, 10);
return !!atoi(value);
}
static char *opts_ini_load(const char *section, const char *name, const char *value, char **parse_file) {
char *error = nullptr;
static char *opts_ini_load(const char *section, const char *name, const char *value) {
char *error = NULL;
bool found = false;
/*
* undef all of these because they may still be defined like in my
* case they where.
*/
*/
#undef GMQCC_TYPE_FLAGS
#undef GMQCC_TYPE_OPTIMIZATIONS
#undef GMQCC_TYPE_WARNS
/* deal with includes */
if (!strcmp(section, "includes")) {
static const char *include_error_beg = "failed to open file `";
static const char *include_error_end = "' for inclusion";
FILE *file = fopen(value, "r");
found = true;
if (!file) {
vec_append(error, strlen(include_error_beg), include_error_beg);
vec_append(error, strlen(value), value);
vec_append(error, strlen(include_error_end), include_error_end);
} else {
if (opts_ini_parse(file, &opts_ini_load, &error, parse_file) != 0)
found = false;
/* Change the file name */
mem_d(*parse_file);
*parse_file = util_strdup(value);
fclose(file);
}
}
/* flags */
#define GMQCC_TYPE_FLAGS
#define GMQCC_DEFINE_FLAG(X) \
@ -340,15 +267,6 @@ static char *opts_ini_load(const char *section, const char *name, const char *va
}
#include "opts.def"
/* Werror-individuals */
#define GMQCC_TYPE_WARNS
#define GMQCC_DEFINE_FLAG(X) \
if (!strcmp(section, "errors") && !strcmp(name, #X)) { \
opts_set(opts.werror, WARN_##X, opts_ini_bool(value)); \
found = true; \
}
#include "opts.def"
/* optimizations */
#define GMQCC_TYPE_OPTIMIZATIONS
#define GMQCC_DEFINE_FLAG(X,Y) \
@ -360,29 +278,24 @@ static char *opts_ini_load(const char *section, const char *name, const char *va
/* nothing was found ever! */
if (!found) {
if (strcmp(section, "includes") &&
strcmp(section, "flags") &&
strcmp(section, "warnings") &&
if (strcmp(section, "flags") &&
strcmp(section, "warnings") &&
strcmp(section, "optimizations"))
{
static const char *invalid_section = "invalid_section `";
vec_append(error, strlen(invalid_section), invalid_section);
vec_append(error, strlen(section), section);
vec_push(error, '`');
} else if (strcmp(section, "includes")) {
static const char *invalid_variable = "invalid_variable `";
static const char *in_section = "` in section: `";
vec_append(error, strlen(invalid_variable), invalid_variable);
vec_append(error, strlen(name), name);
vec_append(error, strlen(in_section), in_section);
vec_append(error, strlen(section), section);
vec_push(error, '`');
vec_upload(error, "invalid section `", 17);
vec_upload(error, section, strlen(section));
vec_push (error, '`');
vec_push (error, '\0');
} else {
static const char *expected_something = "expected something";
vec_append(error, strlen(expected_something), expected_something);
vec_upload(error, "invalid variable `", 18);
vec_upload(error, name, strlen(name));
vec_push (error, '`');
vec_upload(error, " in section: `", 14);
vec_upload(error, section, strlen(section));
vec_push (error, '`');
vec_push (error, '\0');
}
}
vec_push(error, '\0');
return error;
}
@ -396,29 +309,27 @@ void opts_ini_init(const char *file) {
* gmqcc.ini
* gmqcc.cfg
*/
char *error = nullptr;
char *parse_file = nullptr;
char *error;
size_t line;
FILE *ini;
FILE *ini;
if (!file) {
/* try ini */
if (!(ini = fopen((file = "gmqcc.ini"), "r")))
if (!(ini = file_open((file = "gmqcc.ini"), "r")))
/* try cfg */
if (!(ini = fopen((file = "gmqcc.cfg"), "r")))
if (!(ini = file_open((file = "gmqcc.cfg"), "r")))
return;
} else if (!(ini = fopen(file, "r")))
} else if (!(ini = file_open(file, "r")))
return;
con_out("found ini file `%s`\n", file);
parse_file = util_strdup(file);
if ((line = opts_ini_parse(ini, &opts_ini_load, &error, &parse_file)) != 0) {
if ((line = opts_ini_parse(ini, &opts_ini_load, &error)) != 0) {
/* there was a parse error with the ini file */
con_printmsg(LVL_ERROR, parse_file, line, 0 /*TODO: column for ini error*/, "error", error);
con_printmsg(LVL_ERROR, file, line, "error", error);
vec_free(error);
}
mem_d(parse_file);
fclose(ini);
}
file_close(ini);
}

101
opts.def
View file

@ -1,15 +1,36 @@
/*
* Copyright (C) 2012
* Wolfgang Bumiller
* Dale Weiler
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef GMQCC_DEFINE_FLAG
# error "bad opts.def usage"
# define GMQCC_DEFINE_FLAG(x)
#endif
/* codegen flags */
#ifdef GMQCC_TYPE_FLAGS
GMQCC_DEFINE_FLAG(OVERLAP_LOCALS)
GMQCC_DEFINE_FLAG(DARKPLACES_STRING_TABLE_BUG)
GMQCC_DEFINE_FLAG(ADJUST_VECTOR_FIELDS)
GMQCC_DEFINE_FLAG(FTEPP)
GMQCC_DEFINE_FLAG(FTEPP_PREDEFS)
GMQCC_DEFINE_FLAG(FTEPP_MATHDEFS)
GMQCC_DEFINE_FLAG(FTEPP_INDIRECT_EXPANSION)
GMQCC_DEFINE_FLAG(RELAXED_SWITCH)
GMQCC_DEFINE_FLAG(SHORT_LOGIC)
GMQCC_DEFINE_FLAG(PERL_LOGIC)
@ -22,36 +43,18 @@
GMQCC_DEFINE_FLAG(CORRECT_LOGIC)
GMQCC_DEFINE_FLAG(TRUE_EMPTY_STRINGS)
GMQCC_DEFINE_FLAG(FALSE_EMPTY_STRINGS)
GMQCC_DEFINE_FLAG(UTF8)
GMQCC_DEFINE_FLAG(BAIL_ON_WERROR)
GMQCC_DEFINE_FLAG(LOOP_LABELS)
GMQCC_DEFINE_FLAG(UNTYPED_NIL)
GMQCC_DEFINE_FLAG(PERMISSIVE)
GMQCC_DEFINE_FLAG(VARIADIC_ARGS)
GMQCC_DEFINE_FLAG(LEGACY_VECTOR_MATHS)
GMQCC_DEFINE_FLAG(EXPRESSIONS_FOR_BUILTINS)
GMQCC_DEFINE_FLAG(RETURN_ASSIGNMENTS)
GMQCC_DEFINE_FLAG(UNSAFE_VARARGS)
GMQCC_DEFINE_FLAG(TYPELESS_STORES)
GMQCC_DEFINE_FLAG(SORT_OPERANDS)
GMQCC_DEFINE_FLAG(EMULATE_STATE)
GMQCC_DEFINE_FLAG(ARITHMETIC_EXCEPTIONS)
GMQCC_DEFINE_FLAG(SPLIT_VECTOR_PARAMETERS)
GMQCC_DEFINE_FLAG(DEFAULT_ERASEABLE)
#endif
/* warning flags */
#ifdef GMQCC_TYPE_WARNS
GMQCC_DEFINE_FLAG(UNINITIALIZED_GLOBAL)
GMQCC_DEFINE_FLAG(DEBUG)
GMQCC_DEFINE_FLAG(UNUSED_VARIABLE)
GMQCC_DEFINE_FLAG(UNUSED_COMPONENT)
GMQCC_DEFINE_FLAG(USED_UNINITIALIZED)
GMQCC_DEFINE_FLAG(UNKNOWN_CONTROL_SEQUENCE)
GMQCC_DEFINE_FLAG(EXTENSIONS)
GMQCC_DEFINE_FLAG(FIELD_REDECLARED)
GMQCC_DEFINE_FLAG(MISSING_RETURN_VALUES)
GMQCC_DEFINE_FLAG(INVALID_PARAMETER_COUNT)
GMQCC_DEFINE_FLAG(TOO_FEW_PARAMETERS)
GMQCC_DEFINE_FLAG(LOCAL_SHADOWS)
GMQCC_DEFINE_FLAG(LOCAL_CONSTANTS)
GMQCC_DEFINE_FLAG(VOID_VARIABLES)
@ -61,7 +64,7 @@
GMQCC_DEFINE_FLAG(EFFECTLESS_STATEMENT)
GMQCC_DEFINE_FLAG(END_SYS_FIELDS)
GMQCC_DEFINE_FLAG(ASSIGN_FUNCTION_TYPES)
GMQCC_DEFINE_FLAG(CPP)
GMQCC_DEFINE_FLAG(PREPROCESSOR)
GMQCC_DEFINE_FLAG(MULTIFILE_IF)
GMQCC_DEFINE_FLAG(DOUBLE_DECLARATION)
GMQCC_DEFINE_FLAG(CONST_VAR)
@ -69,58 +72,18 @@
GMQCC_DEFINE_FLAG(TERNARY_PRECEDENCE)
GMQCC_DEFINE_FLAG(UNKNOWN_PRAGMAS)
GMQCC_DEFINE_FLAG(UNREACHABLE_CODE)
GMQCC_DEFINE_FLAG(CPP)
GMQCC_DEFINE_FLAG(UNKNOWN_ATTRIBUTE)
GMQCC_DEFINE_FLAG(RESERVED_NAMES)
GMQCC_DEFINE_FLAG(UNINITIALIZED_CONSTANT)
GMQCC_DEFINE_FLAG(DIFFERENT_QUALIFIERS)
GMQCC_DEFINE_FLAG(DIFFERENT_ATTRIBUTES)
GMQCC_DEFINE_FLAG(DEPRECATED)
GMQCC_DEFINE_FLAG(PARENTHESIS)
GMQCC_DEFINE_FLAG(UNSAFE_TYPES)
GMQCC_DEFINE_FLAG(BREAKDEF)
GMQCC_DEFINE_FLAG(CONST_OVERWRITE)
GMQCC_DEFINE_FLAG(DIRECTIVE_INMACRO)
GMQCC_DEFINE_FLAG(BUILTINS)
GMQCC_DEFINE_FLAG(INEXACT_COMPARES)
#endif
#ifdef GMQCC_TYPE_OPTIMIZATIONS
GMQCC_DEFINE_FLAG(PEEPHOLE, 1)
GMQCC_DEFINE_FLAG(TAIL_RECURSION, 1)
GMQCC_DEFINE_FLAG(OVERLAP_LOCALS, 3)
GMQCC_DEFINE_FLAG(LOCAL_TEMPS, 3)
GMQCC_DEFINE_FLAG(GLOBAL_TEMPS, 3)
GMQCC_DEFINE_FLAG(STRIP_CONSTANT_NAMES, 1)
GMQCC_DEFINE_FLAG(OVERLAP_STRINGS, 2)
GMQCC_DEFINE_FLAG(CALL_STORES, 3)
GMQCC_DEFINE_FLAG(VOID_RETURN, 1)
GMQCC_DEFINE_FLAG(VECTOR_COMPONENTS, 1)
GMQCC_DEFINE_FLAG(CONST_FOLD_DCE, 2)
GMQCC_DEFINE_FLAG(CONST_FOLD, 0) /* cannot be turned off */
#endif
#ifdef GMQCC_TYPE_OPTIONS
GMQCC_DEFINE_FLAG(O)
GMQCC_DEFINE_FLAG(OUTPUT)
GMQCC_DEFINE_FLAG(QUIET)
GMQCC_DEFINE_FLAG(G)
GMQCC_DEFINE_FLAG(STANDARD)
GMQCC_DEFINE_FLAG(DEBUG)
GMQCC_DEFINE_FLAG(DUMPFIN)
GMQCC_DEFINE_FLAG(DUMP)
GMQCC_DEFINE_FLAG(FORCECRC)
GMQCC_DEFINE_FLAG(FORCED_CRC)
GMQCC_DEFINE_FLAG(PP_ONLY)
GMQCC_DEFINE_FLAG(MAX_ARRAY_SIZE)
GMQCC_DEFINE_FLAG(ADD_INFO)
GMQCC_DEFINE_FLAG(PROGSRC)
GMQCC_DEFINE_FLAG(COVERAGE)
GMQCC_DEFINE_FLAG(STATE_FPS)
GMQCC_DEFINE_FLAG(PEEPHOLE, 1)
GMQCC_DEFINE_FLAG(LOCALTEMPS, 1)
GMQCC_DEFINE_FLAG(TAIL_RECURSION, 1)
GMQCC_DEFINE_FLAG(TAIL_CALLS, 2)
#endif
/* some cleanup so we don't have to */
#undef GMQCC_TYPE_FLAGS
#undef GMQCC_TYPE_WARNS
#undef GMQCC_TYPE_OPTIONS
#undef GMQCC_TYPE_OPTIMIZATIONS
#undef GMQCC_DEFINE_FLAG

4929
parser.c Normal file

File diff suppressed because it is too large Load diff

6416
parser.cpp

File diff suppressed because it is too large Load diff

View file

@ -1,84 +0,0 @@
#ifndef GMQCC_PARSER_HDR
#define GMQCC_PARSER_HDR
#include "gmqcc.h"
#include "lexer.h"
#include "ast.h"
#include "intrin.h"
#include "fold.h"
struct parser_t;
#define parser_ctx(p) ((p)->lex->tok.ctx)
struct parser_t {
parser_t();
~parser_t();
void remove_ast();
lex_file *lex;
int tok;
bool ast_cleaned;
std::vector<ast_expression *> globals;
std::vector<ast_expression *> fields;
std::vector<ast_function *> functions;
size_t translated;
/* must be deleted first, they reference immediates and values */
std::vector<ast_value *> accessors;
ast_value *nil;
ast_value *reserved_version;
size_t crc_globals;
size_t crc_fields;
ast_function *function;
ht aliases;
/* All the labels the function defined...
* Should they be in ast_function instead?
*/
std::vector<ast_label*> labels;
std::vector<ast_goto*> gotos;
std::vector<const char *> breaks;
std::vector<const char *> continues;
/* A list of hashtables for each scope */
std::vector<ht> variables;
ht htfields;
ht htglobals;
std::vector<ht> typedefs;
/* not to be used directly, we use the hash table */
std::vector<ast_expression*> _locals;
std::vector<size_t> _blocklocals;
std::vector<std::unique_ptr<ast_value>> _typedefs;
std::vector<size_t> _blocktypedefs;
std::vector<lex_ctx_t> _block_ctx;
/* we store the '=' operator info */
const oper_info *assign_op;
/* magic values */
ast_value *const_vec[3];
/* pragma flags */
bool noref;
/* collected information */
size_t max_param_count;
fold m_fold;
intrin m_intrin;
};
/* parser.c */
char *parser_strdup (const char *str);
ast_expression *parser_find_global(parser_t *parser, const char *name);
#endif

250
stat.cpp
View file

@ -1,250 +0,0 @@
#include <string.h>
#include <stdlib.h>
#include "gmqcc.h"
/*
* strdup does it's own malloc, we need to track malloc. We don't want
* to overwrite malloc though, infact, we can't really hook it at all
* without library specific assumptions. So we re implement strdup.
*/
char *stat_mem_strdup(const char *src, bool empty) {
size_t len = 0;
char *ptr = nullptr;
if (!src)
return nullptr;
len = strlen(src);
if ((!empty ? len : true) && (ptr = (char*)mem_a(len + 1))) {
memcpy(ptr, src, len);
ptr[len] = '\0';
}
return ptr;
}
/*
* The reallocate function for resizing vectors.
*/
void _util_vec_grow(void **a, size_t i, size_t s) {
vector_t *d = nullptr;
size_t m = 0;
void *p = nullptr;
if (*a) {
d = vec_meta(*a);
m = 2 * d->allocated + i;
p = mem_r(d, s * m + sizeof(vector_t));
} else {
m = i + 1;
p = mem_a(s * m + sizeof(vector_t));
((vector_t*)p)->used = 0;
}
d = (vector_t*)p;
d->allocated = m;
*a = d + 1;
}
void _util_vec_delete(void *data) {
mem_d(vec_meta(data));
}
/*
* Hash table for generic data, based on dynamic memory allocations
* all around. This is the internal interface, please look for
* EXPOSED INTERFACE comment below
*/
struct hash_node_t {
char *key; /* the key for this node in table */
void *value; /* pointer to the data as void* */
hash_node_t *next; /* next node (linked list) */
};
size_t hash(const char *key);
size_t util_hthash(hash_table_t *ht, const char *key) {
return hash(key) % ht->size;
}
static hash_node_t *_util_htnewpair(const char *key, void *value) {
hash_node_t *node;
if (!(node = (hash_node_t*)mem_a(sizeof(hash_node_t))))
return nullptr;
if (!(node->key = util_strdupe(key))) {
mem_d(node);
return nullptr;
}
node->value = value;
node->next = nullptr;
return node;
}
/*
* EXPOSED INTERFACE for the hashtable implementation
* util_htnew(size) -- to make a new hashtable
* util_htset(table, key, value, sizeof(value)) -- to set something in the table
* util_htget(table, key) -- to get something from the table
* util_htdel(table) -- to delete the table
*/
hash_table_t *util_htnew(size_t size) {
hash_table_t *hashtable = nullptr;
if (size < 1)
return nullptr;
if (!(hashtable = (hash_table_t*)mem_a(sizeof(hash_table_t))))
return nullptr;
if (!(hashtable->table = (hash_node_t**)mem_a(sizeof(hash_node_t*) * size))) {
mem_d(hashtable);
return nullptr;
}
hashtable->size = size;
memset(hashtable->table, 0, sizeof(hash_node_t*) * size);
return hashtable;
}
void util_htseth(hash_table_t *ht, const char *key, size_t bin, void *value) {
hash_node_t *newnode = nullptr;
hash_node_t *next = nullptr;
hash_node_t *last = nullptr;
next = ht->table[bin];
while (next && next->key && strcmp(key, next->key) > 0)
last = next, next = next->next;
/* already in table, do a replace */
if (next && next->key && strcmp(key, next->key) == 0) {
next->value = value;
} else {
/* not found, grow a pair man :P */
newnode = _util_htnewpair(key, value);
if (next == ht->table[bin]) {
newnode->next = next;
ht->table[bin] = newnode;
} else if (!next) {
last->next = newnode;
} else {
newnode->next = next;
last->next = newnode;
}
}
}
void util_htset(hash_table_t *ht, const char *key, void *value) {
util_htseth(ht, key, util_hthash(ht, key), value);
}
void *util_htgeth(hash_table_t *ht, const char *key, size_t bin) {
hash_node_t *pair = ht->table[bin];
while (pair && pair->key && strcmp(key, pair->key) > 0)
pair = pair->next;
if (!pair || !pair->key || strcmp(key, pair->key) != 0)
return nullptr;
return pair->value;
}
void *util_htget(hash_table_t *ht, const char *key) {
return util_htgeth(ht, key, util_hthash(ht, key));
}
void *code_util_str_htgeth(hash_table_t *ht, const char *key, size_t bin);
void *code_util_str_htgeth(hash_table_t *ht, const char *key, size_t bin) {
hash_node_t *pair;
size_t len, keylen;
int cmp;
keylen = strlen(key);
pair = ht->table[bin];
while (pair && pair->key) {
len = strlen(pair->key);
if (len < keylen) {
pair = pair->next;
continue;
}
if (keylen == len) {
cmp = strcmp(key, pair->key);
if (cmp == 0)
return pair->value;
if (cmp < 0)
return nullptr;
pair = pair->next;
continue;
}
cmp = strcmp(key, pair->key + len - keylen);
if (cmp == 0) {
uintptr_t up = (uintptr_t)pair->value;
up += len - keylen;
return (void*)up;
}
pair = pair->next;
}
return nullptr;
}
/*
* Free all allocated data in a hashtable, this is quite the amount
* of work.
*/
void util_htrem(hash_table_t *ht, void (*callback)(void *data)) {
size_t i = 0;
for (; i < ht->size; ++i) {
hash_node_t *n = ht->table[i];
hash_node_t *p;
/* free in list */
while (n) {
if (n->key)
mem_d(n->key);
if (callback)
callback(n->value);
p = n;
n = p->next;
mem_d(p);
}
}
/* free table */
mem_d(ht->table);
mem_d(ht);
}
void util_htrmh(hash_table_t *ht, const char *key, size_t bin, void (*cb)(void*)) {
hash_node_t **pair = &ht->table[bin];
hash_node_t *tmp;
while (*pair && (*pair)->key && strcmp(key, (*pair)->key) > 0)
pair = &(*pair)->next;
tmp = *pair;
if (!tmp || !tmp->key || strcmp(key, tmp->key) != 0)
return;
if (cb)
(*cb)(tmp->value);
*pair = tmp->next;
mem_d(tmp->key);
mem_d(tmp);
}
void util_htrm(hash_table_t *ht, const char *key, void (*cb)(void*)) {
util_htrmh(ht, key, util_hthash(ht, key), cb);
}
void util_htdel(hash_table_t *ht) {
util_htrem(ht, nullptr);
}

1281
test.c Normal file

File diff suppressed because it is too large Load diff

1305
test.cpp

File diff suppressed because it is too large Load diff

View file

@ -1,18 +0,0 @@
#define ACCUMULATE_FUNCTION(FUNC) \
[[accumulate]] void FUNC ()
ACCUMULATE_FUNCTION(foo) {
print("hello ");
}
ACCUMULATE_FUNCTION(foo) {
print("accumulation ");
}
ACCUMULATE_FUNCTION(foo) {
print("world\n");
}
void main() {
foo();
}

View file

@ -1,5 +0,0 @@
I: accumulate.qc
D: test function accumulation
T: -execute
C: -std=gmqcc -fftepp
M: hello accumulation world

View file

@ -1,34 +0,0 @@
float alias_1 = 3.14;
void alias_2() {
print("alias_2\n");
}
[[alias("alias_2")]] void alias_2_aliased();
[[alias("alias_1")]] float alias_1_aliased;
// alias to an alias?
vector alias_3;
[[alias("alias_3")]] vector alias_3_aliased;
// expected output
// alias_2
// 3.14
void main() {
alias_2_aliased();
alias_3_aliased= '1 2 3';
print(
ftos(
alias_1_aliased
),
"\n"
);
print(
"x ", ftos(alias_3_aliased_x), "\n",
"y ", ftos(alias_3_aliased_y), "\n",
"z ", ftos(alias_3_aliased_z), "\n"
);
}

View file

@ -1,9 +0,0 @@
I: aliases.qc
D: test aliases
T: -execute
C: -std=gmqcc
M: alias_2
M: 3.14
M: x 1
M: y 2
M: z 3

View file

@ -1,13 +0,0 @@
const float huge = 340282346638528859811704183484516925440.000000; // FLT_MAX
#ifdef DIVBYZERO
const float a = 1.0 / 0.0;
#endif
#ifdef OVERFLOW
const float a = huge * huge;
#endif
#ifdef UNDERFLOW
const float a = 1 / huge;
#endif

View file

@ -1,4 +0,0 @@
I: arithexcept.qc
D: arithmetic exceptions (divide by zero)
T: -fail
C: -std=fteqcc -farithmetic-exceptions -DDIVBYZERO

View file

@ -1,4 +0,0 @@
I: arithexcept.qc
D: arithmetic exceptions (overflow)
T: -fail
C: -std=fteqcc -farithmetic-exceptions -DOVERFLOW

View file

@ -1,4 +0,0 @@
I: arithexcept.qc
D: arithmetic exceptions (underflow)
T: -fail
C: -std=fteqcc -farithmetic-exceptions -DUNDERFLOW

View file

@ -1,3 +1,7 @@
void print(...) = #1;
string ftos (float) = #2;
entity() spawn = #3;
float glob[7];
.float above;

View file

@ -1,7 +1,7 @@
I: arrays2.qc
D: initialized arrays
I: arrays.qc
D: array accessors and functionality
T: -execute
C: -std=fteqcc
M: 10 20 30 40 50 60 70
M: 100 200 300 400 500 600 0
M: Hello World
M: 1001 1101 1201 1301 1401 1501
M: 1001 1101 1201 1301 1401 1501 1601
M: 1001 1101 1201 1301 1401 1501

View file

@ -1,18 +0,0 @@
float glob1[7] = { 10, 20, 30, 40, 50, 60, 70 };
float glob2[7] = { 100, 200, 300, 400, 500, 600 };
string globs[] = { "Hello ", "World" };
void main() {
float i;
print(ftos(glob1[0]));
for (i = 1; i != 7; ++i)
print(" ", ftos(glob1[i]));
print("\n");
print(ftos(glob2[0]));
for (i = 1; i != 7; ++i)
print(" ", ftos(glob2[i]));
print("\n");
print(globs[0], globs[1], "\n");
}

View file

@ -1,7 +0,0 @@
I: arrays.qc
D: array accessors and functionality
T: -execute
C: -std=fteqcc
M: 1001 1101 1201 1301 1401 1501
M: 1001 1101 1201 1301 1401 1501 1601
M: 1001 1101 1201 1301 1401 1501

View file

@ -1,37 +0,0 @@
void main() {
float a; a = 1;
float b; b = 1;
float c; c = 1;
float d; d = 1;
vector e; e = '1 1 1';
vector f; f = '1 1 1';
#ifdef __STD_FTEQCC__
a &~= 1; // 0
#else
a &= ~1; // 0
#endif
#ifdef __STD_GMQCC__
b &= ~1; // 0
c &= ~d; // 0
#else
b &~= 1; // 0
c &~= 1; // 0
#endif
#ifdef __STD_FTEQCC__
f &~= e; // '0 0 0'
#else
f &= ~e; // '0 0 0'
#endif
#ifdef __STD_GMQCC__
e &= ~e; // '0 0 0'
#else
e &~= e; // '0 0 0'
#endif
print("a: ", ftos(a), "\nb: ",
ftos(b), "\nc: ",
ftos(c), "\n");
print("e: ", vtos(e), "\n");
print("f: ", vtos(f), "\n");
}

View file

@ -1,11 +0,0 @@
# used to test the builtins
I: bitnot.qc
D: test bitwise not operators (fteqcc operators)
T: -execute
C: -std=fteqcc
E: $null
M: a: 0
M: b: 0
M: c: 0
M: e: '0 0 0'
M: f: '0 0 0'

View file

@ -1,11 +0,0 @@
# used to test the builtins
I: bitnot.qc
D: test bitwise not operators (gmqcc operators)
T: -execute
C: -std=gmqcc -fftepp
E: $null
M: a: 0
M: b: 0
M: c: 0
M: e: '0 0 0'
M: f: '0 0 0'

View file

@ -1,3 +1,6 @@
void print(...) = #1;
string ftos (float) = #2;
void test(float brkat, float contat) {
float i;

View file

@ -1,3 +1,5 @@
void(string) print = #1;
void() main = {
print("hello world");
}

View file

@ -4,4 +4,6 @@ D: test builtin functions
T: -execute
C: -std=gmqcc
E: $null
F: builtins failed
S: builtins worked
M: hello world

View file

@ -1,3 +1,6 @@
void(string, ...) print = #1;
string(float) ftos = #2;
float(float x, float y, float z) sum = {
return x + y + z;
};
@ -8,5 +11,5 @@ void(float a, float b, float c) main = {
sum(sum(sum(a, b, c), b, sum(a, b, c)), b, sum(a, b, sum(a, b, c))),
sum(sum(a, b, c), b, c));
print(ftos(f), "\n");
};

View file

@ -3,4 +3,6 @@ D: test calls
T: -execute
C: -std=gmqcc
E: -float 100 -float 200 -float 300
F: calls failed
S: calls worked
M: 4600

View file

@ -2,7 +2,7 @@ I: correct-logic.qc
D: vector logic flags
T: -execute
C: -std=fteqcc -fshort-logic
M: ! & | i N
M: ! & | i N
M: 0, 0 -> 1 0 0 0 1
M: 0, x -> 1 0 1 0 1
M: x, 0 -> 0 0 1 1 0

View file

@ -2,7 +2,7 @@ I: correct-logic.qc
D: vector logic flags
T: -execute
C: -std=fteqcc
M: ! & | i N
M: ! & | i N
M: 0, 0 -> 1 0 0 0 1
M: 0, x -> 1 0 1 0 1
M: x, 0 -> 0 0 1 1 0

View file

@ -2,7 +2,7 @@ I: correct-logic.qc
D: vector logic flags
T: -execute
C: -std=fteqcc -fcorrect-logic -fshort-logic
M: ! & | i N
M: ! & | i N
M: 0, 0 -> 1 0 0 0 1
M: 0, x -> 1 0 1 0 1
M: x, 0 -> 0 0 1 1 0

View file

@ -2,7 +2,7 @@ I: correct-logic.qc
D: vector logic flags
T: -execute
C: -std=fteqcc -fcorrect-logic
M: ! & | i N
M: ! & | i N
M: 0, 0 -> 1 0 0 0 1
M: 0, x -> 1 0 1 0 1
M: x, 0 -> 0 0 1 1 0

View file

@ -1,3 +1,6 @@
void print(...) = #1;
string ftos (float) = #2;
float test_s_not (vector s) { return !s; }
float test_s_and (vector s, vector t) { return s && t; }
float test_s_or (vector s, vector t) { return s || t; }

View file

@ -1,3 +1,6 @@
void print(...) = #1;
string ftos (float) = #2;
void test(vector a, vector b) {
print(ftos((a && b) + (a && b)), " ");
print(ftos((a || b) + (a || b)), " ");

View file

@ -1,21 +0,0 @@
// builtins for the standalone qcvm included with gmqcc
// in exec.c These should be updated to reflect the new
// builtins. I no event shall you even consider adding
// these individually per test.
void (string str, ...) print = #1;
string (float val) ftos = #2;
entity () spawn = #3;
void (entity ent) kill = #4;
string (vector vec) vtos = #5;
void (string str) error = #6;
float (vector vec) vlen = #7;
string (entity ent) etos = #8;
float (string str) stof = #9;
string (...) strcat = #10;
float (string str1, string str2) strcmp = #11;
vector (vector vec) normalize = #12;
float (float val) sqrt = #13;
float (float val) floor = #14;
float (float val1, float val2) pow = #15;
vector (string str) stov = #16;

View file

@ -1,27 +0,0 @@
entity self;
.float f;
..float fp;
...float fpp;
void try(entity e, ...float pp) {
print("and: ", ftos( e.(e.(e.pp)) ), "\n");
}
typedef float Float;
void try2(entity e, ...Float pp) {
print("and: ", ftos( e.(e.(e.pp)) ), "\n");
}
// whereas the varargs are tested in vararg tests
void main() {
self = spawn();
self.f = 123;
self.fp = f;
self.fpp = fp;
print(ftos( self.(self.fp) ), "\n");
print(ftos( self.(self.(self.fpp)) ), "\n");
try(self, fpp);
try2(self, fpp);
}

View file

@ -1,8 +0,0 @@
I: dots.qc
D: TOKEN_DOTS disambiguation
T: -execute
C: -std=fteqcc
M: 123
M: 123
M: and: 123
M: and: 123

View file

@ -1,68 +0,0 @@
enum {
// this behaviour is confusing, but I like that
// we support it.
__ = (__ - 1),
A = (__ + 1),
B,
C
};
enum {
D = C + B,
E = C + C,
F = C + D,
};
enum {
G = (B + F), H = (C + F),
I = (D + F), J = (B + I)
};
enum {
K = A + B - C + D - E + F *
G - H + I - J + A - B -
J + A,
L,
M,
N
};
enum : flag {
F1, /* = 1 << 1 */
F2, /* = 1 << 2 */
F3 /* = 1 << 3 */
};
/* reversed enumeration */
enum : reverse {
R1, // 3
R2, // 2
R3, // 1
R4 // 0
};
void main() {
print(ftos(A), "\n");
print(ftos(B), "\n");
print(ftos(C), "\n");
print(ftos(D), "\n");
print(ftos(E), "\n");
print(ftos(F), "\n");
print(ftos(G), "\n");
print(ftos(H), "\n");
print(ftos(I), "\n");
print(ftos(J), "\n");
print(ftos(K), "\n");
print(ftos(L), "\n");
print(ftos(M), "\n");
print(ftos(N), "\n");
print(ftos(F1), "\n");
print(ftos(F2), "\n");
print(ftos(F3), "\n");
print(ftos(R1), "\n");
print(ftos(R2), "\n");
print(ftos(R3), "\n");
print(ftos(R4), "\n");
};

View file

@ -1,25 +0,0 @@
I: enum.qc
D: enumerations
T: -execute
C: -std=fteqcc
M: 0
M: 1
M: 2
M: 3
M: 4
M: 5
M: 6
M: 7
M: 8
M: 9
M: 10
M: 11
M: 12
M: 13
M: 2
M: 4
M: 8
M: 3
M: 2
M: 1
M: 0

View file

@ -1,3 +1,6 @@
void(string, ...) print = #1;
string(float) ftos = #2;
void(float a, float b) main = {
if (a == b) print("eq,");
if (a != b) print("ne,");

View file

@ -3,4 +3,6 @@ D: test equality
T: -execute
C: -std=gmqcc
E: -float 100 -float 200
F: equality failed
S: equality worked
M: ne,lt,le,

View file

@ -1,14 +0,0 @@
void main() {
float hundy = __builtin_pow(10, 2); // 10^2 == 100
print(ftos(hundy), "\n"); // prints: 100
hundy = pow(10, 2);
print(ftos(hundy), "\n");
hundy -= 90; // 100-90 = 10
print(ftos(hundy ** 2), "\n"); // prints: 100
print(ftos(pow(hundy, 2)), "\n"); // prints: 100
hundy = 10.0f;
print(ftos(__builtin_exp(hundy)), "\n"); // prints: 22026.5
}

View file

@ -1,11 +0,0 @@
# used to test the builtins
I: exponentiation.qc
D: test exponentiation operator and __builtin_pow
T: -execute
C: -std=gmqcc
E: $null
M: 100
M: 100
M: 100
M: 100
M: 22026.5

View file

@ -1,7 +0,0 @@
/* empty line required */
void print(string, ...) = #__LINE__ - 1;
void main(string input) {
print(input, "\n");
}

View file

@ -1,7 +0,0 @@
I: exprforbuiltins.qc
D: expressions for builtins
T: -execute
C: -std=gmqcc -fftepp -fftepp-predefs
F: -no-defs
E: -string test
M: test

View file

@ -1,13 +0,0 @@
.float field;
.float getfield() {
return field;
}
void() main = {
entity e = spawn();
e.field = 42;
print(ftos(e.(getfield())), "\n");
.float memptr = getfield();
print(ftos(e.memptr), "\n");
}

View file

@ -1,6 +0,0 @@
I: fieldfuncs.qc
D: test fields with functions
T: -execute
C: -std=fte
M: 42
M: 42

View file

@ -1,3 +1,6 @@
void(string, string) print = #1;
entity() spawn = #3;
.string a;
.string b;
..string ps;

View file

@ -3,5 +3,7 @@ D: test field paramaters
T: -execute
C: -std=qcc
E: $null
F: field paramaters fail
S: field paramaters work
M: bar
M: foo

View file

@ -1,13 +0,0 @@
void main() {
float j;
for (j = 0; j < 2; ++j)
print("+");
for (float i = 0; i < 5; ++i)
print("*");
for (;;) {
print("\n");
break;
}
}

View file

@ -1,5 +0,0 @@
I: forloop.qc
D: test for loops
T: -execute
C: -std=gmqcc
M: ++*****

View file

@ -1,20 +0,0 @@
$frame frame1 frame2
float time;
entity self;
.float frame;
.float nextthink;
.void() think;
// Mixing syntax, = is optional.
void frame1_func_mixed_no_assign() [$frame1, frame2_func_mixed_no_assign] {}
void frame2_func_mixed_no_assign() [$frame2, frame2_func_mixed_no_assign] {}
void frame1_func_mixed() =[$frame1, frame2_func_mixed] {}
void frame2_func_mixed() =[$frame2, frame2_func_mixed] {}
void() frame1_func_old =[$frame1, frame2_func_old] {}
void() frame2_func_old =[$frame2, frame2_func_old] {}

View file

@ -1,4 +0,0 @@
I: framemacro.qc
D: test frame macros
T: -compile
C: -std=gmqcc

View file

@ -1,3 +1,5 @@
void(string, string) print = #1;
string() getter = {
return "correct";
};

View file

@ -3,4 +3,6 @@ D: test functions as paramaters
T: -execute
C: -std=gmqcc
E: $null
F: functions as paramaters failed
S: functions as paramaters passed
M: correct

View file

@ -1,32 +0,0 @@
// correct execution order:
// label_3
// label_2
// label_4
// label_3
// label_1
// label_5
void main() {
float x = 1;
float y = 2;
goto label_3;
:label_1; print("label_1", "\n"); goto label_5;
:label_2; print("label_2", "\n"); goto label_4;
:label_3; print("label_3", "\n");
// will goto label_2
goto (x == y) ? label_1 : label_2;
:label_4; print("label_4", "\n");
{
x = 1;
y = 1;
// will goto label_1
// then goes label_5
goto label_3;
}
:label_5; print("label_5", "\n");
}

View file

@ -1,10 +0,0 @@
I: goto.qc
D: test goto (both normal and computed)
T: -execute
C: -std=gmqcc
M: label_3
M: label_2
M: label_4
M: label_3
M: label_1
M: label_5

View file

@ -1,3 +1,5 @@
void(string, ...) print = #1;
void(float c) main = {
if (c == 1)
print("One\n");

View file

@ -3,4 +3,6 @@ D: test if statement
T: -execute
C: -std=gmqcc
E: -float 2
F: if statement failed
S: if statement passed
M: Two

View file

@ -1,7 +0,0 @@
void main() {
const float a = 1.0 / 3.0;
const float b = 0.33333333333;
if (a == b) {
// Should trigger warning
}
}

View file

@ -1,4 +0,0 @@
I: inexact-local.qc
D: inexact comparisons
T: -fail
C: -std=gmqcc -Winexact-compares -Wall -Werror

View file

@ -1,8 +0,0 @@
const float a = 1.0 / 3.0;
const float b = 0.33333333333;
void main() {
if (a == b) {
// Should trigger warning
}
}

Some files were not shown because too many files have changed in this diff Show more