Version 19 of tcc4tcl

Updated 2019-06-11 19:14:40 by AMG

tcc4tcl (Tiny C Compiler for Tcl) is a Tcl extension that provides an interface to TCC.

It is a fork of tcltcc by Mark Janssen.

It is licensed under the terms of the LGPL v2.1 (or later).

Homepage: http://chiselapp.com/user/rkeene/repository/tcc4tcl/index

tcc version

AMG: The current version of tcc4tcl (0.30) directly includes a patched copy of tcc 0.9.26 from early 2013. The most recent version of tcc is 0.9.27 from late 2017. I would very much like to upgrade to 0.9.27 because of the many improvements, but doing so breaks virtually all the tcc patches found in [L1 ].

32-bit Windows link failure

AMG: With tcc4tcl 0.30 (tcc 0.9.26) compiled with 32-bit MXE GCC 5.4.0, or with the official build [L2 ] (tried with [L3 ]), I get the following error:

% tcc4tcl::cproc test {Tcl_WideInt a int b} Tcl_WideInt {return a << b;}
tcc: error: undefined symbol '__ashldi3'
relocating failed

This is only a problem in Windows, not Linux. The cause is twofold. (1) gcc is prefixing all the symbol names in libtcc1.a with an extra underscore that tcc is not expecting, and (2) gcc is producing PE-format object files, and tcc only knows how to link ELF object files.

The solution is to apply this patch:

diff -ur tcc4tcl-0.30-old/Makefile.in tcc4tcl-0.30-new/Makefile.in
--- tcc4tcl-0.30-old/Makefile.in        2017-10-13 15:37:05.000000000 -0500
+++ tcc4tcl-0.30-new/Makefile.in        2019-03-13 15:04:27.510092464 -0500
@@ -8,6 +8,8 @@
 CPP = @CPP@
 AR = @AR@
 RANLIB = @RANLIB@
+OBJCOPY = @OBJCOPY@
+OBJDUMP = @OBJDUMP@
 CFLAGS = @CFLAGS@ @SHOBJFLAGS@
 CPPFLAGS = @CPPFLAGS@ -I$(shell cd @srcdir@ && pwd) -I$(shell cd @srcdir@ && pwd)/tcc -I$(shell pwd)/tcc @DEFS@ @SHOBJCPPFLAGS@
 LDFLAGS = @LDFLAGS@
@@ -29,7 +31,7 @@
 host_os = @host_os@
 @SET_MAKE@
 
-all: $(TARGET) tcc/libtcc1.a
+all: $(TARGET) tcc/libtcc1-elf.a
 
 tcc/config.h:
         if [ "$(srcdir)" = "." ]; then \
@@ -46,6 +48,17 @@
         -$(MAKE) -C tcc tcc@EXEEXT@
         $(MAKE) -C tcc libtcc1.a
 
+# tcc supports dynamically loading object code from ELF, not from PE, so on some
+# platforms it is necessary to convert its runtime support library to ELF.
+tcc/libtcc1-elf.a: tcc/libtcc1.a
+        if $(OBJDUMP) -a $< | grep -q ' file format pei\?-x86-64$$'; then \
+                $(OBJCOPY) --remove-leading-char -O elf64-x86-64 $< $@; \
+        elif $(OBJDUMP) -a $< | grep -q ' file format pei\?-i386$$'; then \
+                $(OBJCOPY) --remove-leading-char -O elf32-i386 $< $@; \
+        else \
+                cp -f $< $@; \
+        fi
+
 tcc4tcl.o: $(srcdir)/tcc4tcl.c $(srcdir)/tcc/tcc.h $(srcdir)/tcc/libtcc.h tcc/config.h
         $(CC) $(CPPFLAGS) $(CFLAGS) -o tcc4tcl.o -c $(srcdir)/tcc4tcl.c
 
@@ -60,7 +73,7 @@
         -$(RANLIB) tcc4tcl-static.new.a
         mv tcc4tcl-static.new.a tcc4tcl-static.a
 
-install: $(TARGET) pkgIndex.tcl $(srcdir)/tcc4tcl.tcl $(srcdir)/tcc4critcl.tcl tcc/libtcc1.a $(shell echo $(srcdir)/tcc/include/*) $(shell echo $(srcdir)/tcc/win32/lib/*.c) $(srcdir)/headers.awk $(srcdir)/patch-headers.sh
+install: $(TARGET) pkgIndex.tcl $(srcdir)/tcc4tcl.tcl $(srcdir)/tcc4critcl.tcl tcc/libtcc1-elf.a $(shell echo $(srcdir)/tcc/include/*) $(shell echo $(srcdir)/tcc/win32/lib/*.c) $(srcdir)/headers.awk $(srcdir)/patch-headers.sh
         $(INSTALL) -d "$(DESTDIR)$(PACKAGE_INSTALL_DIR)"
         $(INSTALL) -d "$(DESTDIR)$(PACKAGE_INSTALL_DIR)/lib"
         $(INSTALL) -d "$(DESTDIR)$(PACKAGE_INSTALL_DIR)/include"
@@ -68,7 +81,7 @@
         $(INSTALL) -m 0644 pkgIndex.tcl "$(DESTDIR)$(PACKAGE_INSTALL_DIR)"
         $(INSTALL) -m 0644 $(srcdir)/tcc4tcl.tcl "$(DESTDIR)$(PACKAGE_INSTALL_DIR)"
         $(INSTALL) -m 0644 $(srcdir)/tcc4critcl.tcl "$(DESTDIR)$(PACKAGE_INSTALL_DIR)"
-        $(INSTALL) -m 0644 tcc/libtcc1.a "$(DESTDIR)$(PACKAGE_INSTALL_DIR)/lib"
+        $(INSTALL) -m 0644 tcc/libtcc1-elf.a "$(DESTDIR)$(PACKAGE_INSTALL_DIR)/lib/libtcc1.a"
         $(INSTALL) -m 0644 $(shell echo $(srcdir)/tcc/win32/lib/*.c) "$(DESTDIR)$(PACKAGE_INSTALL_DIR)/lib"
         $(INSTALL) -m 0644 $(shell echo $(srcdir)/tcc/include/*) "$(DESTDIR)$(PACKAGE_INSTALL_DIR)/include"
         @if ! echo "_WIN32" | $(CPP) $(CPPFLAGS) - | grep '^_WIN32$$' >/dev/null; then \
diff -ur tcc4tcl-0.30-old/configure.ac tcc4tcl-0.30-new/configure.ac
--- tcc4tcl-0.30-old/configure.ac        2017-10-13 15:37:16.000000000 -0500
+++ tcc4tcl-0.30-new/configure.ac        2019-03-13 15:06:59.156101031 -0500
@@ -39,6 +39,7 @@
 
         TARGET="tcc4tcl-static.a"
 fi
+AC_CHECK_TOOL([OBJDUMP], [objdump])
 AC_SUBST(TARGET)
 AC_SUBST(TCC4TCL_TARGET)
 AC_SUBST(TCC_EXTRA_CFLAGS)
diff -ur tcc4tcl-0.30-old/test.tcl tcc4tcl-0.30-new/test.tcl
--- tcc4tcl-0.30-old/test.tcl        2017-10-13 15:37:05.000000000 -0500
+++ tcc4tcl-0.30-new/test.tcl        2019-03-13 15:09:06.231108210 -0500
@@ -15,6 +15,9 @@
 # This should work
 tcc4tcl::cproc test3 {int i} int { return(i+42); }
 
+# Check for libtcc1 functionality
+tcc4tcl::cproc testlibtcc1 {double x} int { return(x); }
+
 # Multiple arguments
 tcc4tcl::cproc add {int a int b} int { return(a+b); }
 

Be cautious with this patch since it intentionally contains tabs and end-of-line whitespace, all of which have been stripped off by the wiki. Let me know if you want the original file with correct formatting.

After applying the patch, run "autoconf" to regenerate the configure script.

I have confirmed that the above patch fixes 32-bit Windows and does not break 64-bit Windows, 32-bit Linux, or 64-bit Linux.

Note: Even though I say that the error only happens on 32-bit Windows, as far as I can tell, this is only because libtcc1 is not needed as much on 64-bit Windows. If I dug deeper, I could probably find a test case that would break 64-bit Windows too, i.e. a case where libtcc1 is used on both 32- and 64-bit CPUs.

Variadic arguments

AMG: Is there any facility to access a cproc command's full objv argument list?

Answer: No. Bypass cproc and instead use ccode directly to create functions matching the signature shown below. Then use linktclcommand to arrange for Tcl_CreateObjCommand to be called on those functions. Note that the created Tcl commands are in the global namespace by default, not in the current namespace eval namespace, unless the namespace is explicitly given in the command name.

typedef int Tcl_ObjCmdProc(
        ClientData clientData,
        Tcl_Interp *interp,
        int objc,
        Tcl_Obj *const objv[]);

Compilation

dbohdan 2015-03-16: As of version 0.23 you can compile tcc4tcl on Linux thus:

#!/bin/sh
set -e
version=0.23
release="tcc4tcl-$version"
url="http://rkeene.org/devel/tcc4tcl/${release}.tar.gz"
curl "$url" -o "${release}.tar.gz" # -O may not be unavailable.
tar zxvf "$release.tar.gz"
cd $release
./configure
make

dbohdan: Updated the script once more to hammer Roy Keene's server less.